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