Серьезно: вы действительно не хотите этого делать.
Но для опытных пользователей Python полезно это понять, поэтому я объясню это.
Ячейки и freevarsявляются значениями, назначенными при создании замыкания.Например,
def f():
a = 1
def func():
print(a)
return func
f
возвращает замыкание на основе func
, сохраняя ссылку на a
.Эта ссылка хранится в ячейке (на самом деле во freevar, но какая из них зависит от реализации).Вы можете проверить это:
myfunc = f()
# ('a',)
print(myfunc.__code__.co_freevars)
# (<cell at 0xb7abce84: int object at 0x82b1de0>,)
print(myfunc.__closure__)
(«ячейки» и «freevars» очень похожи. У Freevars есть имена, где ячейки имеют индексы. Они оба хранятся в func.__closure__
, причем ячейки идут первыми.Мы заботимся только о freevars
здесь, поскольку именно это __class__
.)
Как только вы поймете это, вы увидите, как на самом деле работает super ().Любая функция, содержащая вызов super
, на самом деле является замыканием с произвольным именем __class__
(которое также добавляется, если вы сами ссылаетесь на __class__
):
class foo:
def bar(self):
print(__class__)
(Предупреждение: этоздесь все становится злым.)
Эти клетки видны в func.__closure__
, но доступны только для чтения;ты не можешь это изменить.Единственный способ изменить это - создать новую функцию, что делается с помощью конструктора types.FunctionType
.Тем не менее, ваша __init__
функция вообще не имеет __class__
freevar - поэтому нам нужно добавить ее.Это означает, что мы также должны создать новый объект кода.
Код ниже делает это.Я добавил базовый класс B
для наглядности.Этот код делает некоторые предположения, например.что у __init__
еще нет свободной переменной с именем __class__
.
Здесь есть еще один хак: кажется, нет конструктора для типа ячейки.Чтобы обойти это, создается фиктивная функция C.dummy
, в которой есть необходимая переменная ячейки.
import types
class B(object):
def __init__(self):
print("base")
class C(B):
def dummy(self): __class__
def __init__(self):
print('calling __init__')
super().__init__()
def MakeCodeObjectWithClass(c):
"""
Return a copy of the code object c, with __class__ added to the end
of co_freevars.
"""
return types.CodeType(c.co_argcount, c.co_kwonlyargcount, c.co_nlocals,
c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)
new_code = MakeCodeObjectWithClass(__init__.__code__)
old_closure = __init__.__closure__ or ()
C.__init__ = types.FunctionType(new_code, globals(), __init__.__name__,
__init__.__defaults__, old_closure + (C.dummy.__closure__[0],))
if __name__ == '__main__':
c = C()