Я получил неожиданное закрытие при создании вложенного класса.Я подозреваю, что это связано с метаклассами, супер или обоими.Это определенно связано с тем, как создаются замыкания.Я использую 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). - Объект в закрывающей ячейке не является функцией.
- Объект не является
Metaclass
или Base
, но некоторый объект типа Metaclass
, такой как Base
, равен (# 1). - Объект на самом деле является объектом типа вложенного
Subclass
(# 1). - Закрывающие ячейки для
t1.subclass.__init__
и t1.subclass2.__init__
одинаковы, даже если они из двух разных классов (# 1). - Когда я не вкладываю создание
Subclass
(# 1) тогда замыкание не создается. - Когда я не вызываю
super(...).__init__
in Subclass.init__
замыкание не создается (# 2). - Если я назначаю нет
__metaclass__
и наследовать от object
, то же поведение проявляется (# 3). - Объект в ячейке закрытия для
t3.other.__init__
равен t3.other
(# 3). - то же самое происходит, если метакласс не имеет
__init__
(# 4). - samТакое поведение происходит, если
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
Хорошие вещи!