如何解决如何静默地让嵌套类知道它的外部类通过将所有必需的代码放在父类或元类中?
我制作了一个符号类,可以帮助我通过使用元类来制作符号常量
>>> 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,4)}
但是,如果我想通过将这些符号嵌套在外部类中来将其用于枚举,则需要一种方法来使符号内部类知道它们的外部类。使用当前的实现,会发生以下情况:
>>> 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
类知道它们的外部 Animal
类,但又不会使它们的声明复杂化。我想要所有需要的代码,隐藏在超类(Symbol
或 Animal
的超类)或其元类中。
解决方法
与名称相反的 __qualname__
包括有关封闭类或函数的信息:
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
可以涵盖大量用例。有时我只是觉得它有点矫枉过正,并使用一些更简单的临时事物,但是一旦您求助于自定义元类,那就不再“更简单”了,并且您可能会遇到一些枚举已经解决的极端情况。
真正获取关于封闭类的信息
现在,无论枚举如何,如果想要使用元类并获得有关封闭类主体的信息 - 如本问题的标题所示,事情就变得棘手了。对于在类体定义内部,类体本身的变量是局部变量,全局变量指向模块全局变量:当封闭作用域是函数时,没有 nonlocal
的等价物。>
如果设置为类属性的对象需要有关其所有者类的信息,Python 会实现强大的 descriptor procotol,由语言内置 property
使用。您只需要在对象的类中定义一个 __get__
方法,并注意所有者属性:不需要元类:
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
现在,如果想要在嵌套类创建时获得有关封闭作用域的信息,这是可行的,但是需要内省元类'__new__
中的堆栈,并在封闭范围是另一个类的主体。一个强烈的暗示是它是否定义了 locals
和 __module__
键。然后你可以访问封闭类的命名空间,但是......封闭类本身还不存在:
__qualname__
因此,如果只需要封闭类 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,**kw)
...:
In [42]: class A:
...: class B(metaclass=M): pass
...:
{'__module__': '__main__','__qualname__': 'A'}
,则可以使用 __name__
并完成它。但是,如果您只想要名称,那么 __qualname__
本身已经设置在嵌套的类命名空间中。
如果想要一个对 __qualname__
本身的有效引用,在创建嵌套类本身时是不可能的,因为此时外部类不存在:它只会在结束时创建当然,它自己的身体的定义。
幸运的是,如果属性是类,并且在其元类中定义了 __class__
特殊方法,则描述符机制将起作用:
__set_name__
再次:问题的最后一部分只是为了那些想要获得对外部类的引用而不是获取名称的其他目的的人。现在,检查描述符协议,使用 In [51]: class Symbol(type):
...: def __set_name__(cls,name):
...: assert name == cls.__name__
...: cls.owner = owner
# it turns out __get__ is not needed for __set_name__ to work
...: #def __get__(cls,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
方法可以提供此类信息,而根本不需要元类。
您需要让 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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。