python - 为什么直接向 numpy 数组添加新属性不起作用,但通过子类化却可以?

标签 python numpy inheritance subclass

我想创建一个行为类似于 numpy 数组但拥有附加方法/属性的类,并且一直在阅读但不完全理解 numpy 的 guide关于 ndarray 的子类化。该网页上有一个示例,内容如下:

import numpy as np

class RealisticInfoArray(np.ndarray):

    def __new__(cls, input_array, info=None):
        # Input array is an already formed ndarray instance
        # We first cast to be our class type
        obj = np.asarray(input_array).view(cls)
        # add the new attribute to the created instance
        obj.info = info
        # Finally, we must return the newly created object:
        return obj

    def __array_finalize__(self, obj):
        # see InfoArray.__array_finalize__ for comments
        if obj is None: return
        self.info = getattr(obj, 'info', None)

我很困惑为什么会有这些线条

        obj = np.asarray(input_array).view(cls)
        # add the new attribute to the created instance
        obj.info = info

不要加注

AttributeError: 'numpy.ndarray' object has no attribute 'info'

我已阅读Add an attribute to a Numpy array in runtime它与用 C 实现的 numpy 数组有关。故事就这样结束了吗? Python 如何“知道” np.array 是用 C 实现的,而不是可以轻松添加新属性的 Python 类?

最佳答案

C 实现的类必须不遗余力地拥有 __dict__ (这是存储动态定义的属性的位置);他们可以做到这一点,但他们通常不会这样做,除非他们试图模拟允许它的其他类型(例如 functools.partial 允许您分配任意属性,因为常规函数允许它,并且它试图保持兼容),因为他们有更有效的方法来存储预定义的属性集(通常作为 PyObject header 中的原始值或指针)。

省略 __dict__每个实例节省了一个指针的内存开销(4-8 字节),加上实际 dict 的成本本身(即使是 64 位 CPython 3.9.5 上的空 __dict__ 也是 104 字节)。对于您创建许多实例的简单类型,包括 __dict__当它几乎从未被使用时会大量增加开销。例如,CPython 3.9.5 x64 float消耗 24 个字节来存储 8 个字节的“真实”数据,这意味着 16 个字节是开销;如果它允许任意属性分配,即使 __dict__ ,开销也会从 16 字节跳到 24 字节。是延迟创建的,如果它不是延迟创建的(通过删除对“允许__dict__但它可能尚未初始化”的检查来加速其他代码,该检查必须在每次访问时执行)开销将从24 到 128 字节(加上分配器开销浪费未严格分配的字节的两倍机会,但由于舍入和碎片问题而丢失),所有这些都只是 8 字节的“真实”数据。存储五百万float s 会将 40 MB 的原始 C 成本转移到 __dict__ -less CPython 成本为 120 MB(忽略实际容纳它们的容器;这将增加至少 40 MB)到 680 MB,所有这些都取决于您可能想要在一个上定义任意属性的可能性。 > 其中。

另一方面,用户定义的类有 __dict__默认情况下(这是默认情况下唯一存储属性的地方,无论是在 __init__ 中定义还是由类的使用者手动添加),并且仅当类及其所有父类,定义一个类级别__slots__ (并且只有当它们全部从 '__dict__' 中省略 __slots__ 时)。

要回答您的具体问题“Python 如何“知道” np.array 是用 C 实现的,而不是您可以轻松添加新属性的 Python 类?”,至少对于 CPython 而言,它测试 tp_dictoffset on the instance's class非零;如果为零,则该类的实例缺少 __dict__添加任意属性是不合法的,如果它非零,它会告诉解释器距离 PyObject 的开头(或结尾,如果为负数)有多少字节。它需要查找 header 才能找到 __dict__指针。 tp_dictoffset在定义类时初始化,在 C 实现的类想要支持任意属性的情况下手动初始化,并由解释器机制代表您初始化用户定义的类。

关于python - 为什么直接向 numpy 数组添加新属性不起作用,但通过子类化却可以?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69947020/

相关文章:

python - 选择 pandas DataFrame 中具有特定值的列

python - 使用来自 2 个 numpy 矩阵的数据绘制直方图

python - 根据 lambda 函数将 NumPy 整数列表值映射到列表

java - Java 中的多重继承,因为所有类都从 Object 类扩展?

C++我应该覆盖派生类中的复制构造函数吗

python - python2.6中没有索引的字符串格式化

python - tensorflow DynamicRnnEstimator - 无前缀或后缀

python - 为 Pandas 数据框中的每一行运行一次函数

python - Pandas Dtypewarning : How do I find the dtype of different cells in a column?

C++ 基模板类虚方法没有出现在派生中?