ネストされたクラスを作成するときに予期しないクロージャが発生しました。これはメタクラス、スーパー、またはその両方に関連するものだと思います。これは、クロージャがどのように作成されるかに間違いなく関係しています。私はpython2.7を使用しています。
これが私が見ているのと同じ問題を示す5つの単純化された例です(それらはすべて最初のものから構築されます):
例1:
class Metaclass(type):
def __init__(self, name, bases, dict):
self.CONST = 5
class Base(object):
__metaclass__=Metaclass
def __init__(self):
"Set things up."
class Subclass(Base):
def __init__(self, name):
super(Subclass, self).__init__(self)
self.name = name
def other(self, something): pass
class Test(object):
def setup(self):
class Subclass(Base):
def __init__(self, name):
super(Subclass, self).__init__(self)
self.name = name
def other(self, something): pass
self.subclass = Subclass
class Subclass2(Base):
def __init__(self, name):
super(Subclass, self).__init__(self)
self.subclass2 = Subclass2
"0x%x" % id(Metaclass)
# '0x8257f74'
"0x%x" % id(Base)
# '0x825814c'
t=Test()
t.setup()
"0x%x" % id(t.subclass)
# '0x8258e8c'
"0x%x" % id(t.subclass2)
# '0x825907c'
t.subclass.__init__.__func__.__closure__
# (<cell at 0xb7d33d4c: Metaclass object at 0x8258e8c>,)
t.subclass.other.__func__.__closure__
# None
t.subclass2.__init__.__func__.__closure__
# (<cell at 0xb7d33d4c: Metaclass object at 0x8258e8c>,)
Subclass.__init__.__func__.__closure__
# None
例2:
class Test2(object):
def setup(self):
class Subclass(Base):
def __init__(self, name):
self.name = name
def other(self, something): pass
self.subclass = Subclass
t2=Test2()
t2.setup()
t2.subclass.__init__.__func__.__closure__
# None
例3:
class Test3(object):
def setup(self):
class Other(object):
def __init__(self):
super(Other, self).__init__()
self.other = Other
class Other2(object):
def __init__(self): pass
self.other2 = Other2
t3=Test3()
t3.setup()
"0x%x" % id(t3.other)
# '0x8259734'
t3.other.__init__.__func__.__closure__
# (<cell at 0xb7d33e54: type object at 0x8259734>,)
t3.other2.__init__.__func__.__closure__
# None
例4:
class Metaclass2(type): pass
class Base2(object):
__metaclass__=Metaclass2
def __init__(self):
"Set things up."
class Base3(object):
__metaclass__=Metaclass2
class Test4(object):
def setup(self):
class Subclass2(Base2):
def __init__(self, name):
super(Subclass2, self).__init__(self)
self.subclass2 = Subclass2
class Subclass3(Base3):
def __init__(self, name):
super(Subclass3, self).__init__(self)
self.subclass3 = Subclass3
class Subclass4(Base3):
def __init__(self, name):
super(Subclass4, self).__init__(self)
self.subclass4 = Subclass4
"0x%x" % id(Metaclass2)
# '0x8259d9c'
"0x%x" % id(Base2)
# '0x825ac9c'
"0x%x" % id(Base3)
# '0x825affc'
t4=Test4()
t4.setup()
"0x%x" % id(t4.subclass2)
# '0x825b964'
"0x%x" % id(t4.subclass3)
# '0x825bcac'
"0x%x" % id(t4.subclass4)
# '0x825bff4'
t4.subclass2.__init__.__func__.__closure__
# (<cell at 0xb7d33d04: Metaclass2 object at 0x825b964>,)
t4.subclass3.__init__.__func__.__closure__
# (<cell at 0xb7d33e9c: Metaclass2 object at 0x825bcac>,)
t4.subclass4.__init__.__func__.__closure__
# (<cell at 0xb7d33ddc: Metaclass2 object at 0x825bff4>,)
例5:
class Test5(object):
def setup(self):
class Subclass(Base):
def __init__(self, name):
Base.__init__(self)
self.subclass = Subclass
t5=Test5()
t5.setup()
"0x%x" % id(t5.subclass)
# '0x8260374'
t5.subclass.__init__.__func__.__closure__
# None
これが私が理解していることです(例を参照):
- メタクラスは継承されるため、のメタクラス
Subclass
を取得しBase
ます。 - 影響を受けるのは影響を受けるだけ
__init__
で、Subclass.other
メソッドは影響を受けません(#1)。 - 削除
Subclass.other
しても違いはありません(#1)。 self.name=name
から削除しSubclass.__init__
ても違いはありません(#1)。- クロージャセル内のオブジェクトは関数ではありません。
- オブジェクトは
Metaclass
またはではありませんが、 (#1)と同じようBase
に、タイプのオブジェクトがあります。Metaclass
Base
- オブジェクトは、実際にはネストされたタイプ
Subclass
(#1)のオブジェクトです。 - とのクロージャーセルは
t1.subclass.__init__
、t1.subclass2.__init__
2つの異なるクラス(#1)からのものですが、同じです。 Subclass
(#1)の作成をネストしない場合、クロージャーは作成されません。- 電話をかけないと
super(...).__init__
、Subclass.init__
クロージャーは作成されません(#2)。 - noを割り当て
__metaclass__
てから継承するとobject
、同じ動作が表示されます(#3)。 - のクロージャセルのオブジェクト
t3.other.__init__
はt3.other
(#3)です。 - メタクラスに
__init__
(#4)がない場合も、同じ動作が発生します。 Base
がない場合__init__
(#4)も同じ動作が発生します。- 例4の3つのサブクラスのクロージャーセルはすべて異なり、それぞれが対応するクラス(#4)と一致します。
- をに
super(...).__init__
置き換えるとBase.__init__(self)
、クロージャーが消えます(#5)。
これが私が理解していないことです:
- なぜクロージャーが設定されるの
__init__
ですか? - なぜクロージャーが他のクロージャーに設定されないのですか?
- クロージャーセル内のオブジェクトが、
__init__
属するクラスに設定されているのはなぜですか? super(...).__init__
が呼び出されたときにのみこれが発生するのはなぜですか?Base.__init__(self)
が呼び出されたときになぜこれが起こらないのですか?- これは実際にはメタクラスの使用とはまったく関係がありますか(おそらく、デフォルトのメタクラスは
type
)ですか?
助けてくれてありがとう!
-エリック
(更新)これが私がその時見つけたものです(ジェイソンの洞察に基づいて):
def something1():
print "0x%x" % id(something1)
def something2():
def something3():
print "0x%x" % id(something1)
print "0x%x" % id(something2)
print "0x%x" % id(something3)
return something3
return something2
something1.__closure__
# None
something1().__closure__
# 0xb7d4056c
# (<cell at 0xb7d33eb4: function object at 0xb7d40df4>,)
something1()().__closure__
# 0xb7d4056c
# (<cell at 0xb7d33fec: function object at 0xb7d40e64>, <cell at 0xb7d33efc: function object at 0xb7d40e2c>)
something1()()()
# 0xb7d4056c
# 0xb7d4056c
# 0xb7d40e9c
# 0xb7d40ed4
まず、関数の名前はそれ自体の本体内のスコープ内にあります。次に、関数は、それらの関数を参照する場合、それらが定義されている関数のクロージャを取得します。
関数名がそのようなスコープにあることに気づいていませんでした。同じことがクラスにも当てはまります。クラスが関数のスコープ内で定義されている場合、クラスのメソッド内でそのクラス名を参照すると、次のように、クラスはそのメソッドの関数のクロージャにバインドされます。
def test():
class Test(object):
def something(self):
print Test
return Test
test()
# <class '__main__.Test'>
test().something.__func__.__closure__
# (<cell at 0xb7d33c2c: type object at 0x825e304>,)
ただし、非関数に対してクロージャを作成できないため、以下は失敗します。
def test():
class Test(object):
SELF=Test
def something(self):
print Test
return Test
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 2, in test
# File "<stdin>", line 3, in Test
# NameError: free variable 'Test' referenced before assignment in enclosing scope
良いもの!