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

2024-03-04

通常,您可以从该类的实例访问常规类属性/字段。然而,当尝试访问一个类时property,会引发 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. 按照方法解析顺序检查实例的类及其超类的属性。它不会继续到类的类(元类)——它遵循继承链。

      1. if the attribute is found in the class and it has a __get__ method, making it a descriptor: the __get__ method is called with the instance and its class as parameters - the returned value is used as the attribute value
        • note:对于使用的类__slots__,每个实例属性都记录在一个特殊的描述符中 - 它存在于类本身中并且具有__get__方法,因此在这一步检索槽类的实例属性
      2. 如果没有__get__方法,它只是跳过类中的搜索。
    2. 检查实例本身:该属性应作为实例中的条目存在__dict__属性。如果是,则返回相应的值。 (__dict__是一个特殊属性,可以在 cPython 中直接访问,但否则会遵循上面的开槽属性的描述符规则)

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

  2. 类(或超类)__getattr__如果存在,则使用属性名称调用方法。它可能返回一个值,或者引发 AttributeError(__getattr__和低水平是不同的__getattribute__,并且更容易定制)

  3. 引发属性错误。

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

(**) 注意。该属性检索算法描述相当完整,但是对于属性分配和删除,而不是检索,描述符存在一些差异,具体取决于它是否具有属性__set__ (or __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)

and:

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

In [45]: a = A()

In [46]: a.cls_prop
Out[46]: True
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么从实例获取类属性会引发 AttributeError? 的相关文章

  • Python 中的随机优化

    我正在尝试结合cvxopt http cvxopt org 优化求解器 和PyMC https github com pymc devs pymc 采样器 解决凸问题随机优化问题 作为参考 安装这两个软件包pip很简单 pip instal
  • 为什么 localhost:5000 在 Flask 中不起作用?

    我正在使用 Flask 应用程序工厂模式 并且有这个 run py 文件 from app import create app app create app if name main app run host localhost debug
  • 将逻辑回归从 R 迁移到 rpy2

    我正在尝试使用 ryp2 进行逻辑回归 我设法执行它 但不知道如何从结果中提取系数和 p 值 我不想在屏幕上打印这些值 而是创建一个函数来独立使用它们 import rpy2 robjects as ro mydata ro r data
  • Python:数百万个小文件的读写速度缓慢

    结论 看来 HDF5 是适合我的目的的方法 基本上 HDF5 是一种用于存储和管理数据的数据模型 库和文件格式 并且旨在处理令人难以置信的大量数据 它有一个名为 python tables 的 Python 模块 链接在下面的答案中 HDF
  • 配置解析器和带 % 的字符串

    愚蠢的问题 当然 简单的答案 我正在使用 configparser 从文件中读取一些字符串 当字符串具有 符号 例如 时 它会抱怨 ConfigParser InterpolationSyntaxError 后必须跟 或 找到 有人熟悉这个
  • PyDev 无法再调试

    我正在使用 eclipse 4 2 1 和 pydev 2 7 1 以前是 2 6 0 一切都工作正常 直到调试器突然停止工作 它打印 pydev debugger 开始 然后根本不运行程序 而是挂起 根据我在其他问题报告中找到的一些信息
  • 为什么比较匹配的字符串比比较不匹配的字符串更快? [复制]

    这个问题在这里已经有答案了 这里有两个测量值 timeit timeit toto 1234 number 100000000 1 8320042459999968 timeit timeit toto toto number 100000
  • 如何使用 cron 作业运行 python 文件

    您好 我创建了一个 python 文件 例如file example py 该文件将输出 sensex 值 假设该文件在linux系统上的路径为 Desktop downloads file example py 我通常会运行该文件pyth
  • python 和回文

    我最近写了一个循环的方法 usr share dict words并使用我的返回回文列表ispalindrome x 方法 这是一些代码 有什么问题吗 它只会停止 10 分钟 然后返回文件中所有单词的列表 def reverse a ret
  • Python:如何将包含对象的列表保存在文件中?

    我尝试创建不同的对象 使用类和对象 并将它们保存在文件中以便稍后编辑或检索它们 然而这就是它的样子 GlobalCategories GlobalContent def LoadData x y import pickle with ope
  • 如何在 Flask-SQLAlchemy 中通过 id 删除记录

    I have users我的 MySql 数据库中的表 这张表有id name and age fields 我怎样才能删除一些记录id 现在我使用以下代码 user User query get id db session delete
  • subprocess.Popen args 参数的最大长度是多少?

    我在用Popen http docs python org library subprocess html using the subprocess modulesubprocess 模块中的函数来执行命令行工具 subprocess Po
  • 将画布的鼠标坐标转换为地理坐标

    我正在尝试使用 Python Tkinter 创建包含意大利所有城市的地图Canvas 我在网上找到了一张意大利地图的图片 其中突出显示了一些城市 并将其插入到我的Canvas 之后 我使用一个函数来确定 2 个突出显示的城市的画布坐标 i
  • matplotlib 的上限/下限

    我想用误差线绘制一些数据点 其中一些数据点只有上限或下限 而不是误差线 所以我尝试使用索引来区分带有误差条的点和带有上限 下限的点 但是 当我尝试这样的事情时 errorbar x i y i yerr ymin i ymax i 我收到错
  • 获取小部件的背景颜色 - 真的

    我无法获取小部件的实际背景颜色 在我的特殊情况下 我在使用 QTabWidget 中的小部件时遇到问题 这是在Windows7上 因此 经典的小部件有一些灰色背景 而选项卡内的小部件通常用白色背景绘制 I tried def bgcolor
  • 使用 nditer 进行浅层迭代

    我有这样一个数组 gt gt gt y np random randint 0 255 2 2 3 gt gt gt array 242 14 211 198 7 0 235 60 81 164 64 236 我必须迭代每个triplet元
  • 在组织内部分发我的 python 模块

    我用 python 制作了一些模块 我想将它们分发到我的组织内 这些模块已经存储在BitBucket中 例如 有什么方法可以使用 pip install 来分发它们吗 正确的方法是什么 您可以从 GitHub 进行 pip 安装 并且应该能
  • 如何在给定目标大小的情况下在 python 中调整图像大小,同时保留纵横比?

    首先 我觉得这是一个愚蠢的问题 对此感到抱歉 目前 我发现计算最佳缩放因子 目标像素数的最佳宽度和高度 同时保留纵横比 的最准确方法是迭代并选择最佳缩放因子 但是必须有更好的方法来做到这一点 一个例子 import cv2 numpy as
  • Python:定义多个相同类型的变量?

    可能是重复的 但至少我无法通过搜索这些术语找到答案 在Python中有没有更快的方法来做到这一点 level1 level2 level3 我试过了 level1 level2 level3 但这似乎创建了该对象的副本 这不是我想要的 和
  • 在 Pandas 中按索引分组

    如何使用 groupby by 索引 1 2 3 它们的顺序相同 并获得属于每个索引范围的列分数的总和 基本上我有这个 index score 1 2 2 2 3 2 1 3 2 3 3 3 我想要的是 index score sum 1

随机推荐