我怎样才能默默地让嵌套 class 知道它的外部 class(通过将所有必需的代码放在父级或元 class 中)?
How can I silently make a nested class aware of it's outer class (by putting all the required code in parent or metaclass)?
我制作了一个符号 class,它可以帮助我使用 metaclasses
来制作符号常量
>>> class SymbolClass(type):
def __repr__(self): return self.__name__
>>> class Symbol(metaclass=SymbolClass): pass
>>> class Spam(Symbol) : pass
>>> class Egg(Symbol) : pass
>>> class Banana(Symbol) : pass
>>> mydict = {Egg:"Where is the bacon", Banana:(2, 3, 4)}
>>> mydict
{Egg: 'Where is the bacon', Banana: (2, 3, 4)}
但是,如果我想通过将此类符号嵌套在外部 class 中来将其用于枚举,我将需要一种方法让符号内部 class 知道它们的外部 class。在当前的实施中,会发生以下情况:
>>> class Animal:
class Dog(Symbol): pass
class Cat(Symbol): pass
>>> print(repr(Animal.Dog))
Dog
从打印 str(Animal.Dog)
得到这个回复是可以的,但是当打印 repr(Animal.Dog)
时,我想得到 Animal.Dog
.
那么,我怎样才能使嵌套的 Animal.Dog
和 Animal.Cat
classes 知道它们的外部 Animal
class,而不会使它们复杂化宣言。我想拥有所有需要的代码,隐藏在超级 classes(Symbol
或 Animal
的超级 class)或它们的元 classes 中.
您需要 Animal
继承自 Enum
:
from enum import Enum
class Animal(Enum):
class Dog(Symbol): pass
class Cat(Symbol): pass
>>> print(repr(Animal.Dog))
<Animal.Dog: Dog>
>>> print(Animal.Dog)
Animal.Dog
如果您想要不同的 str()
和 repr()
,您需要定义它们:
class Animal(Enum):
#
def __repr__(self):
return "%s.%s" % (self.__class__.__name__, self._name_)
#
def __str__(self):
return self._name_
#
class Dog(Symbol): pass
class Cat(Symbol): pass
与name对比的__qualname__
包含了封装classes或函数的信息:
In [13]: class Symbol(type):
...: def __repr__(cls):
...: return cls.__qualname__
...:
...:
In [14]: class Dog(metaclass=Symbol): pass
In [15]: Dog
Out[15]: Dog
In [16]: class Animal:
...: class Dog(metaclass=Symbol): pass
...:
In [17]: Animal.Dog
Out[17]: Animal.Dog
当然,如果你真的想使用枚举,Python的enum.Enum
可以涵盖大量的用例。有时我只是觉得它有点矫枉过正,并使用一些更简单的 ad-hoc 东西,但是一旦你求助于自定义元 class,它就不再“更简单”了,你可能会遇到一些极端情况该枚举已经解决了。
真正获取有关封闭的信息 class
现在,无论枚举如何,如果有人想使用元class 并了解有关封闭 class body 的信息 - 如本问题的标题所示,事情更棘手。对于 class body 定义内部, class body 中的变量本身是局部变量,全局变量指向模块 globals:没有等效项nonlocal
当封闭范围是一个函数时。
如果设置为 class 属性的 object 需要有关其所有者 class 的信息,Python 实现强大的 descriptor procotol,由语言 built-in property
。您所需要的只是在 object 的 class 中定义一个 __get__
方法,并记下所有者属性:不需要元 classes:
In [56]: class Symbol: # not a metaclass
...: def __get__(self, instance, owner):
...: self.owner = owner
...: return self
...: def __set_name__(self, owner, name):
...: self.name = name
...: def __repr__(self):
...: return f"{self.owner.__name__}.{self.name}"
...:
...:
In [57]: class Animal:
...: Dog = Symbol()
...:
In [58]: Animal.Dog
Out[58]: Animal.Dog
现在,如果在嵌套 class 创建时想要有关封闭范围的信息,这是可行的,但需要在 metaclass' __new__
并查找封闭范围中的 locals
是否是另一个 class 的 body。一个强烈的暗示是它是否定义了 __module__
和 __qualname__
键。然后你可以访问封闭的 class 命名空间,但是......封闭的 class 本身还不存在:
In [40]: import inspect
In [41]: class M(type):
...: def __new__(mcls, name, bases, ns, **kw):
...: enclosing_frame = inspect.stack()[1].frame
...: print (enclosing_frame.f_locals)
...: return super().__new__(mcls, name, bases, ns, **kw)
...:
In [42]: class A:
...: class B(metaclass=M): pass
...:
{'__module__': '__main__', '__qualname__': 'A'}
因此,如果只需要封闭的 class __name__
,那么只需使用 __qualname__
即可完成。但是,如果只需要名称,__qualname__
本身就已经设置在嵌套的 class 命名空间中。
如果想要对 __class__
本身的有效引用,则在创建嵌套 class 本身时是不可能的,因为此时外部 class 不存在: 只会在自己定义的末尾创建body,当然。
幸运的是,如果属性是 class,描述符机制将起作用,并且 __set_name__
特殊方法在其元 class:
中定义
In [51]: class Symbol(type):
...: def __set_name__(cls, owner, name):
...: assert name == cls.__name__
...: cls.owner = owner
# it turns out __get__ is not needed for __set_name__ to work
...: #def __get__(cls, instance, owner):
...: # needed so the attribute is recognized as a descriptor (? - actually: to be checked)
...: # return cls
...: def __repr__(cls):
...: return f"{cls.owner.__name__}.{cls.__name__}"
...:
...:
In [52]: class Animal:
...: class Dog(metaclass=Symbol): pass
...:
In [53]: Animal.Dog
Out[53]: Animal.Dog
再说一次:问题的最后一部分只是为了那些想要获得对外部 class 的引用而不是获取名称的其他目的的人。现在,检查使用 __get__
方法的描述符协议是否可以提供此类信息,而根本不需要元 class。
我制作了一个符号 class,它可以帮助我使用 metaclasses
来制作符号常量>>> class SymbolClass(type):
def __repr__(self): return self.__name__
>>> class Symbol(metaclass=SymbolClass): pass
>>> class Spam(Symbol) : pass
>>> class Egg(Symbol) : pass
>>> class Banana(Symbol) : pass
>>> mydict = {Egg:"Where is the bacon", Banana:(2, 3, 4)}
>>> mydict
{Egg: 'Where is the bacon', Banana: (2, 3, 4)}
但是,如果我想通过将此类符号嵌套在外部 class 中来将其用于枚举,我将需要一种方法让符号内部 class 知道它们的外部 class。在当前的实施中,会发生以下情况:
>>> class Animal:
class Dog(Symbol): pass
class Cat(Symbol): pass
>>> print(repr(Animal.Dog))
Dog
从打印 str(Animal.Dog)
得到这个回复是可以的,但是当打印 repr(Animal.Dog)
时,我想得到 Animal.Dog
.
那么,我怎样才能使嵌套的 Animal.Dog
和 Animal.Cat
classes 知道它们的外部 Animal
class,而不会使它们复杂化宣言。我想拥有所有需要的代码,隐藏在超级 classes(Symbol
或 Animal
的超级 class)或它们的元 classes 中.
您需要 Animal
继承自 Enum
:
from enum import Enum
class Animal(Enum):
class Dog(Symbol): pass
class Cat(Symbol): pass
>>> print(repr(Animal.Dog))
<Animal.Dog: Dog>
>>> print(Animal.Dog)
Animal.Dog
如果您想要不同的 str()
和 repr()
,您需要定义它们:
class Animal(Enum):
#
def __repr__(self):
return "%s.%s" % (self.__class__.__name__, self._name_)
#
def __str__(self):
return self._name_
#
class Dog(Symbol): pass
class Cat(Symbol): pass
与name对比的__qualname__
包含了封装classes或函数的信息:
In [13]: class Symbol(type):
...: def __repr__(cls):
...: return cls.__qualname__
...:
...:
In [14]: class Dog(metaclass=Symbol): pass
In [15]: Dog
Out[15]: Dog
In [16]: class Animal:
...: class Dog(metaclass=Symbol): pass
...:
In [17]: Animal.Dog
Out[17]: Animal.Dog
当然,如果你真的想使用枚举,Python的enum.Enum
可以涵盖大量的用例。有时我只是觉得它有点矫枉过正,并使用一些更简单的 ad-hoc 东西,但是一旦你求助于自定义元 class,它就不再“更简单”了,你可能会遇到一些极端情况该枚举已经解决了。
真正获取有关封闭的信息 class
现在,无论枚举如何,如果有人想使用元class 并了解有关封闭 class body 的信息 - 如本问题的标题所示,事情更棘手。对于 class body 定义内部, class body 中的变量本身是局部变量,全局变量指向模块 globals:没有等效项nonlocal
当封闭范围是一个函数时。
如果设置为 class 属性的 object 需要有关其所有者 class 的信息,Python 实现强大的 descriptor procotol,由语言 built-in property
。您所需要的只是在 object 的 class 中定义一个 __get__
方法,并记下所有者属性:不需要元 classes:
In [56]: class Symbol: # not a metaclass
...: def __get__(self, instance, owner):
...: self.owner = owner
...: return self
...: def __set_name__(self, owner, name):
...: self.name = name
...: def __repr__(self):
...: return f"{self.owner.__name__}.{self.name}"
...:
...:
In [57]: class Animal:
...: Dog = Symbol()
...:
In [58]: Animal.Dog
Out[58]: Animal.Dog
现在,如果在嵌套 class 创建时想要有关封闭范围的信息,这是可行的,但需要在 metaclass' __new__
并查找封闭范围中的 locals
是否是另一个 class 的 body。一个强烈的暗示是它是否定义了 __module__
和 __qualname__
键。然后你可以访问封闭的 class 命名空间,但是......封闭的 class 本身还不存在:
In [40]: import inspect
In [41]: class M(type):
...: def __new__(mcls, name, bases, ns, **kw):
...: enclosing_frame = inspect.stack()[1].frame
...: print (enclosing_frame.f_locals)
...: return super().__new__(mcls, name, bases, ns, **kw)
...:
In [42]: class A:
...: class B(metaclass=M): pass
...:
{'__module__': '__main__', '__qualname__': 'A'}
因此,如果只需要封闭的 class __name__
,那么只需使用 __qualname__
即可完成。但是,如果只需要名称,__qualname__
本身就已经设置在嵌套的 class 命名空间中。
如果想要对 __class__
本身的有效引用,则在创建嵌套 class 本身时是不可能的,因为此时外部 class 不存在: 只会在自己定义的末尾创建body,当然。
幸运的是,如果属性是 class,描述符机制将起作用,并且 __set_name__
特殊方法在其元 class:
In [51]: class Symbol(type):
...: def __set_name__(cls, owner, name):
...: assert name == cls.__name__
...: cls.owner = owner
# it turns out __get__ is not needed for __set_name__ to work
...: #def __get__(cls, instance, owner):
...: # needed so the attribute is recognized as a descriptor (? - actually: to be checked)
...: # return cls
...: def __repr__(cls):
...: return f"{cls.owner.__name__}.{cls.__name__}"
...:
...:
In [52]: class Animal:
...: class Dog(metaclass=Symbol): pass
...:
In [53]: Animal.Dog
Out[53]: Animal.Dog
再说一次:问题的最后一部分只是为了那些想要获得对外部 class 的引用而不是获取名称的其他目的的人。现在,检查使用 __get__
方法的描述符协议是否可以提供此类信息,而根本不需要元 class。