Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
import this
def add_this(f):
def wrapped(self, *args, **kwargs):
f.__globals__.update(self.__class__.__dict__)
f.__globals__.update(self.__dict__)
f.__globals__['this'] = self
return f(*args, **kwargs)
return wrapped
class D(metaclass=AddThisMeta):
name = 'Daniel'
def say(phrase):
print("{} says: {}".format(name, phrase))
class C:
name = 'Alex'
@add_this
def say(phrase):
print("{} says: {}".format(this.name, phrase))
class Echo:
name = 'Echo'
@add_this
def say(c, phrase):
c.say(phrase)
print("{} says: {}".format(this.name, phrase))
c = C()
e = Echo()
e.say(c, "does it work?")
Alex says: does it work? Alex says: does it work?
Alex says: does it work? Echo says: does it work?
def add_this(f):
def wrapped(self, *args, **kwargs):
old_this = f.__globals__.pop('this', None)
f.__globals__['this'] = self
result = f(*args, **kwargs)
f.__globals__['this'] = old_this
return result
return wrapped
func.__code__
, но я не вижу для этого стандартных модулей (модуль dis успешно читает func.__code__
, но я не знаю, как превратить изменённый dis.Instruction в байткод). Код будет выглядеть как‐то так:#!/usr/bin/python3.4
import dis
def f(foo):
print(this, foo)
fc = f.__code__
new_code = []
for instruction in dis.get_instructions(fc):
if instruction.opname in {'LOAD_GLOBAL', 'STORE_GLOBAL', 'DELETE_GLOBAL'} and instruction.argval == 'this':
newopname = instruction.opname.replace('GLOBAL', 'FAST')
instruction = dis.Instruction(
opname = newopname,
opcode = dis.opmap[newopname],
arg = 0,
argval = 0,
argrepr = 'this',
offset = instruction.offset,
starts_line = instruction.starts_line,
is_jump_target = instruction.is_jump_target,
)
elif instruction.opname in {'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST'}:
instruction.arg += 1
instruction.argval += 1
# Convert instruction to byte code here.
new_fc = f.__code__.__class__(
fc.co_argcount + 1,
fc.co_kwonlyargcount,
fc.co_nlocals + 1,
fc.co_stacksize,
fc.co_flags,
b''.join(new_code),
fc.co_consts,
fc.co_names,
('this',) + fc.co_varnames,
fc.co_filename,
fc.co_name,
fc.co_firstlineno,
fc.co_lnotab,
fc.co_freevars,
fc.co_cellvars,
)
new_f = f.__class__(new_fc, f.__globals__)
new_f(1, 2)
, но не хватает критического куска на месте «convert instruction to byte code here». Кроме того, байткод официально нестабилен и данный способ может не подходить для не‐CPython реализаций Python.
Ночные кошмары Питона: неявный `this`