gpt4 book ai didi

python - 为什么从实例获取类属性会引发 AttributeError?

转载 作者:行者123 更新时间:2023-12-02 18:23:08 25 4
gpt4 key购买 nike

通常,您可以从该类的实例访问常规类属性/字段。但是,当尝试访问类属性 时,会引发 AttributeError。为什么实例看不到类对象上的属性?

class Meta(type):

@property
def cls_prop(cls):
return True


class A(metaclass=Meta):
cls_attr = True


A.cls_attr # True
A.cls_prop # True
a = A()
a.cls_attr # True
a.cls_prop # AttributeError: 'A' object has no attribute 'cls_prop'

最佳答案

这是由于属性查找的工作方式所致。在试图检索一个实例中的属性,Python 会:

  1. 调用实例的类 __getattribute__(不是元类 __getattribute__),这反过来将:

    1. 按照方法解析顺序检查实例的类及其父类(super class)的属性。它不会进入类的类(元类)——它遵循继承链..

      1. 如果在类中找到该属性并且它有一个__get__ 方法,使其成为一个描述符:__get__ 方法以实例及其类作为参数调用- 返回值用作属性值
        • 注意:对于使用__slots__ 的类,每个实例属性都记录在一个特殊的描述符中——它存在于类本身中并有一个__get__ > 方法,因此在这一步检索开槽类的实例属性
      2. 如果没有 __get__ 方法,它会跳过类中的搜索。
    2. 检查实例本身:该属性应作为实例 __dict__ 属性中的条目存在。如果是,则返回相应的值。 (__dict__ 是一个特殊的属性,它可以在 cPython 中直接访问,否则将遵循上面的开槽属性的描述符规则)

    3. 再次检查类(及其继承层次结构)的属性,这一次,不管它是否具有 __get__ 方法。如果找到,则使用它。类中的这种属性检查直接在类及其父类(super class) __dict__ 中执行,而不是通过以递归方式调用它们自己的 __getattribute__。 (*)

  2. 调用类(或父类(super class))__getattr__ 方法(如果存在)并带有属性名称。它可能会返回一个值,或者引发 AttributeError(__getattr__ 与低级别的 __getattribute__ 不同,更容易定制)

  3. 引发了 AttributeError。

(*) 这是回答您问题的步骤:未在元类中搜索实例中的属性。在上面的代码中,如果您尝试使用 A.cls_prop 作为属性,而不是 A().cls_prop 它将起作用:直接从类中检索属性时,它在上面的检索算法中扮演了“实例”的角色。

(**) 注意。这个属性检索算法的描述是相当完整的,但是对于属性的分配和删除,而不是检索,描述符有一些区别,基于它是否具有__set__(或__del__) 方法,是否使其成为“数据描述符”:非数据描述符(例如在实例的类主体中定义的常规方法)直接分配给实例的字典,因此覆盖和“关闭”方法只是为了那个例子。数据描述符将调用它们的 __set__ 方法。

如何使元类中定义的属性适用于实例:

如您所见,属性访问是高度可定制的,如果您想在实例中工作的元类中定义“类属性”,很容易定制您的代码以使其工作。一种方法是向您的基类(而不是元类)添加一个 __getattr__,它将在元类上查找自定义描述符并调用它们:

class Base(metaclass=Meta):
def __getattr__(self, name):
metacls = type(cls:=type(self))
if hasattr(metacls, name):
metaattr = getattr(metacls, name)
if isinstance(metaattr, property): # customize this check as you want. It is better not to call it for anything that has a `__get__`, as it would retrieve metaclass specific stuff, such as its __init__ and __call__ methods, if those were not defined in the class.
attr = metaattr.__get__(cls, metacls)
return attr
return super().__getattr__(name)

和:

In [44]: class A(Base):
...: pass
...:

In [45]: a = A()

In [46]: a.cls_prop
Out[46]: True

关于python - 为什么从实例获取类属性会引发 AttributeError?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70590552/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com