真剣に:あなたは本当にこれをしたくありません。
ただし、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__)
(「cells」と「freevars」は非常に似ています。Freevarsには名前があり、セルにはインデックスがあります。どちらもに格納されfunc.__closure__
、セルが最初になります。ここでのみ気にします。freevars
それが何で__class__
あるかです。)
それを理解すると、super()が実際にどのように機能するかを確認できます。toの呼び出しを含む関数は、super
実際にはクロージャーであり、freevarという名前が付けられて__class__
います(これは、自分自身を参照する場合にも追加され__class__
ます)。
class foo:
def bar(self):
print(__class__)
(警告:これは物事が悪になるところです。)
これらのセルはに表示されますがfunc.__closure__
、読み取り専用です。変更することはできません。これを変更する唯一の方法は、コンストラクターを使用して実行される新しい関数を作成することですtypes.FunctionType
。ただし、__init__
関数にはfreevarがまったくないため、__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()