(Python)在不期望的情况下创建Closure

时间:2010-07-30 18:38:01

标签: python closures nested-class super

创建嵌套类时出现意外关闭。我怀疑这是与元类,超级或两者相关的东西。它绝对与闭包的创建方式有关。我正在使用python2.7。

以下是五个简化的示例,它们展示了我所看到的相同问题(它们都构建了第一个):

示例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)。
  • 闭包单元格中的对象不是函数。
  • 该对象不是MetaclassBase,但Metaclass类型的某个对象,就像Base一样(#1)。
  • 该对象实际上是嵌套Subclass(#1)类型的对象。
  • t1.subclass.__init__t1.subclass2.__init__的闭包单元格是相同的,即使它们来自两个不同的类别(#1)。
  • 当我没有嵌套Subclass(#1)的创建时,则没有创建闭包。
  • 当我在super(...).__init__中没有打电话给Subclass.init__时,没有创建任何关闭(#2)。
  • 如果我不指定__metaclass__并从object继承,则会显示相同的行为(#3)。
  • t3.other.__init__的闭包单元格中的对象为t3.other(#3)。
  • 如果元类没有__init__(#4),则会发生相同的行为。
  • 如果Base没有__init__(#4),则会发生同样的行为。
  • 示例4中三个子类的闭包单元格都不同,每个都与相应的类(#4)匹配。
  • super(...).__init__替换为Base.__init__(self)时,关闭消失(#5)。

这是我不明白的地方:

  • 为什么要为__init__设置闭包?
  • 为什么不为其他人设置闭包?
  • 为什么闭包单元格中的对象设置为__init__所属的类?
  • 为什么这只会在调用super(...).__init__时发生?
  • 为什么在调用Base.__init__(self)时不会发生这种情况?
  • 这实际上与使用元类有什么关系(可能,因为默认的元类是type)?

感谢您的帮助!

-Eric

(更新)这是我当时发现的东西(基于杰森的洞察力):

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

好东西!

1 个答案:

答案 0 :(得分:4)

  

为什么要为__init__设置闭包?

它引用封闭函数中的局部变量(即Subclass)(即setup)。

  

为什么不为other设置闭包?

因为它没有引用任何封闭函数中的任何局部变量(或参数)。

  

为什么闭包单元格中的对象设置为__init__所属的类?

这是被引用的封闭变量的值。

  

为什么这只会在调用super(...).__init__时发生?

     

为什么在调用Base.__init__(self)时不会发生这种情况?

因为Base不是任何封闭函数中的局部变量。

  

这实际上与使用元类有什么关系吗?

没有