PEP 312 (and thus 308) implemented with a black magic trick
Michele Simionato
mis6 at pitt.edu
Mon Mar 17 16:58:28 EST 2003
Today I had an illumination on how to implement both PEP 312 and 308
in current Python. I think I will share the idea, even if I would *not*
recommend to use such a dirty trick in production code.
Notice that instead of the proposed colon notation for PEP 312
(:x as a shortcut for lambda :x) I use a tilde notation:
~x as a shortcut for lambda :x
This means that I am changing the semantics of the unary operator
"~", which is a Bad Thing: but as I said, this is dirty trick ;)
I am not suggesting it. This is simply a proof of concept.
Here there is an example of usage:
------------------------------------------------------------------
# example.py; will not work from the interpreter
from ternary import if_, RecognizesImplicitLambdas
from math import sqrt
class C(RecognizesImplicitLambdas):
def safesqrt(self,x):
return if_( x>0, ~sqrt(x), ~0) #short-circuiting ternary operator
c=C()
print c.safesqrt(4), c.safesqrt(-4)
--------------------------------------------------------------------
The output of this script is 2.0 and 0, therefore
if_( x>0, ~sqrt(x), ~0) is short-circuiting, as wanted.
Here there is the ternary module:
--------------------------------------------------------------------
# module ternary.py
"PEP 308 and 312 implemented via a metaclass-powered dirty trick"
import inspect,__main__
# the ternary operator:
def if_(cond,f,g):
"Short circuiting ternary operator implemented via callable expressions"
if cond: return f()
else: return g()
# an utility function:
def dedent(block):
"Dedent a block of code, if need there is"""
lines=block.splitlines(); firstline=lines[0]
spaces=len(firstline)-len(firstline.lstrip())
if not spaces: return block
return '\n'.join([line[spaces:] for line in lines])
# the metaclass black magic:
class DirtyTrick(type):
"""Cooperative metaclass that looks at the source code of its instances
and replaces the string '~' with 'lambda :' before the class creation"""
def __new__(meta,name,bases,dic):
for attr in dic.values():
if inspect.isfunction(attr):
code=inspect.getsource(attr)
if code.find('~')==-1: continue # no '~' found, skip
code=code.replace('~','lambda :')
code=dedent(code)+'\n'
exec code in __main__.__dict__,dic # modifies dic
return super(DirtyTrick,meta).__new__(meta,name,bases,dic)
# a convenient base class:
class RecognizesImplicitLambdas:
"Children of this class do recognize implicit lambdas"
__metaclass__=DirtyTrick
--------------------------------------------------------------------------
I am aware of the limitation of this approach, still it is quite cool:
notice that the metaclass changes the source code on the fly *before* the
class creation and automagically will be invoked for any subclass of
RecognizesImplicitLambdas.
Quite impressive, IMHO.
Of course, changing the semantics of the language is never a good idea,
still it is impressive that it is a so easy to perform such a black magic.
Skipping the comments, the ternary module is only 20 lines long!
Python-is-not-as-simple-as-you-could-imagine-ly yours,
--
Michele Simionato - Dept. of Physics and Astronomy
210 Allen Hall Pittsburgh PA 15260 U.S.A.
Phone: 001-412-624-9041 Fax: 001-412-624-9163
Home-page: http://www.phyast.pitt.edu/~micheles/
More information about the Python-list
mailing list