Skip to content

Instantly share code, notes, and snippets.

@remram44
Last active December 18, 2015 03:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save remram44/5718325 to your computer and use it in GitHub Desktop.
Save remram44/5718325 to your computer and use it in GitHub Desktop.
Implicit self
import dis
import struct
import types
class instancemethod(object):
def __init__(self, func):
code = func.func_code
bytecode = code.co_code
# weird stuff here
def make_self_funcs():
container = [None]
def getter():
return container[0]
def setter(self):
container[0] = self
return getter, setter
getter, self.setter = make_self_funcs()
# witchcraft here
def blah():
return getter
cell = blah.func_closure[0]
# Rewrite bytecode
try:
global_number = func.func_code.co_names.index('self') # the global
except ValueError:
pass # Function does not use 'self'
else:
if func.func_closure is not None:
cell_number = len(func.func_closure) # the cell to use instead
else:
cell_number = 0
# LOAD_GLOBAL <global_number> 74 xx xx
# ->
# LOAD_DEREF <cell_number> 88 xx xx
# CALL_FUNCTION 0 83 00 00
# numbers are little endian
print "replacing..."
dis.dis(bytecode)
bytecode = bytecode.replace(
struct.pack('<BH', 0x74, global_number),
struct.pack('<BHBH', 0x88, cell_number, 0x83, 0))
print "with..."
dis.dis(bytecode)
# Create new function object
freevars = code.co_freevars + ('self',)
new_code = types.CodeType(
code.co_argcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags | 0x10,
bytecode,
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
freevars,
code.co_cellvars)
if func.func_closure is None:
closure = (cell,)
else:
closure = func.func_closure + (cell,)
self.new_func = types.FunctionType(
new_code,
func.func_globals,
func.func_name,
func.func_defaults,
closure)
def __get__(self, instance, owner):
self.setter(instance)
return self.new_func
class A(object):
@instancemethod
def foo():
print A, self
A().foo()
@remram44
Copy link
Author

remram44 commented Jun 6, 2013

I didn't test extensively, I was just wondering whether it could be done by only manipulating bytecode and not actual interpreter structures. Thinking about it, another problem with the original is that it's not reentrant (recursive calls would alter 'self' on the caller).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment