2

ネストされたクラスを作成するときに予期しないクロージャが発生しました。これはメタクラス、スーパー、またはその両方に関連するものだと思います。これは、クロージャがどのように作成されるかに間違いなく関係しています。私は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に、タイプのオブジェクトがあります。MetaclassBase
  • オブジェクトは、実際にはネストされたタイプ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

良いもの!

4

1 に答える 1

4

なぜクロージャーが設定されるの__init__ですか?

Subclassこれは、囲んでいる関数(つまり)のローカル変数(つまり)を参照しsetupます。

なぜクロージャーが設定されないのotherですか?

これは、囲んでいる関数のローカル変数(またはパラメーター)を参照していないためです。

クロージャーセル内のオブジェクトが、__init__属するクラスに設定されているのはなぜですか?

これは、参照されている囲んでいる変数の値です。

super(...).__init__が呼び出されたときにのみこれが発生するのはなぜですか?

Base.__init__(self)が呼び出されたときになぜこれが起こらないのですか?

Baseは、囲んでいる関数のローカル変数ではないためです。

これは実際にメタクラスの使用とはまったく関係がありますか?

いいえ。

于 2010-07-30T18:42:01.780 に答える