New Python language abuse: ++x with bytecodehacks
Jeff Epler
jepler at inetnebr.com
Thu Jul 8 08:19:46 EDT 1999
I know just everyone has always neede to write
def f(y):
x=0
while ++x < y:
print x,
print
in Python. Well, you always could write it, but it printed the rather
boring
0 0 0 0 0 0 0 ...........................
which is probably not what you wanted. Well, presenting some code using
Michael Hudson's bytecodehacks. You can still write the above code, but
now it will print
1 2 3 4 5 6 7 8 9
instead.
The trick (and it will only let you implement preincrement and
predecriment) is that the source
++x
will produce distinctive code, along the lines of
LOAD_FAST x
UNARY_POSITIVE
UNARY_POSITIVE
which can be turned by bytecodehacks into
LOAD_FAST x
LOAD_CONST 1
BINARY_ADD
STORE_FAST x
LOAD_FAST x
Hey, it's abusive, and hey, I sure won't use it in my daily life, and hey,
it'll go wrong in at least one circumstance ("+(+x)"). But that doesn't
matter, bytecodehacks made it possible.
Jeff
PS Who wants to suggest inventive uses for +-x and -+x? Heck, why not make
+++---+++x call x.sos() ? I can see it now, calling python methods through
morse code!
from bytecodehacks.code_editor import Function,EditableCode
from bytecodehacks.ops import *
def make_preinc(function):
f = Function(function)
code = f.func_code
cs = code.co_code
i = 0
while i < len(cs)-2:
op = cs[i]
if op.__class__ in (LOAD_FAST, LOAD_GLOBAL, LOAD_ATTR) \
and cs[i+1].__class__ is UNARY_POSITIVE\
and cs[i+2].__class__ is UNARY_POSITIVE:
if 1 in code.co_consts:
a = code.co_consts.index(1)
else:
a = len(code.co_consts)
code.co_consts.append(1)
if op.__class__ is LOAD_FAST:
new_op = [LOAD_FAST(op.arg), LOAD_CONST(a), BINARY_ADD(), STORE_FAST(op.arg), LOAD_FAST(op.arg)]
elif op.__class__ is LOAD_GLOBAL:
new_op = [LOAD_GLOBAL(op.arg), LOAD_CONST(a), BINARY_ADD(), STORE_GLOBAL(op.arg), LOAD_GLOBAL(op.arg)]
elif op.__class__ is LOAD_ATTR:
new_op = [DUP_TOP(), DUP_TOP(), LOAD_ATTR(op.arg), LOAD_CONST(a), BINARY_ADD(),
STORE_ATTR(op.arg), LOAD_ATTR(op.arg)]
cs[i:i+3] = new_op
i=i+len(new_op)
i=i+1
return f.make_function()
# Test
import dis
x=lambda x: (x, ++x)
dis.dis(x)
print
y=make_preinc(x)
dis.dis(y)
print x(1), y(1)
def f(x=10):
y=0
while ++y < x:
print y,
print
dis.dis(Function(f).make_function()); print
g=make_preinc(f)
dis.dis(g)
g()
# vim:ts=4:sw=4
More information about the Python-list
mailing list