gpt4 book ai didi

python - 为什么 __slots__ 在从抽象基类继承时在 Python 2 和 3 中表现不同

转载 作者:行者123 更新时间:2023-11-28 21:51:47 25 4
gpt4 key购买 nike

我创建了以下类以节省内存的方式在平面上存储可变点 - 我需要一个可变的 namedtuple('Point', 'x y') 等价物。由于实例字典很大,我想我会选择 __slots__:

from collections import Sequence

class Point(Sequence):
__slots__ = ('x', 'y')

def __init__(self, x=0, y=0):
self.x = x
self.y = y

def __getitem__(self, item):
return getattr(self, self.__slots__[item])

def __setitem__(self, item, value):
return setattr(self, self.__slots__[item], value)

def __repr__(self):
return 'Point(x=%r, y=%r)' % (self.x, self.y)

def __len__(self):
return 2

在 Python 3 上测试时,一切似乎都正常:

>>> pt = Point(12, 42)
>>> pt[0], pt.y
(12, 42)
>>> pt.x = 5
>>> pt
Point(x=5, y=42)
>>> pt.z = 6
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'

但是在 Python 2 上,我可以设置属性 z,即使它不在槽中也是如此:

>>> pt = Point(12, 42)
>>> pt.z = 5
>>> pt.z
5
>>> pt.__slots__
('x', 'y')
>>> pt.__dict__
{'z': 5}

为什么会这样,为什么 Python 2 和 Python 3 之间存在差异?

最佳答案

Python 2 数据模型在 __slots__ 上表示以下内容:

  • When inheriting from a class without __slots__, the __dict__ attribute of that class will always be accessible, so a __slots__ definition in the subclass is meaningless.

这就是这里发生的事情。在 Python 2 中,collections 模块中的抽象基类根本没有 __slots__:

>>> from collections import Sequence
>>> Sequence.__slots__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'Sequence' has no attribute '__slots__'

这被报告为 issue 11333在 CPython 问题跟踪器中,并在 Python 3.3 中得到修复。

在 Python 3.3+ 中,Sequence 抽象基类现在将 __slots__ 设置为空元组:

>>> from collections import Sequence
>>> Sequence.__slots__
()

因此在 Python 2 中,您不能从 collections 基类继承并同时使用 __slots__ 进行内存高效存储。


但是请注意,即使关于 collections abstract base classes 的文档声称

These ABCs allow us to ask classes or instances if they provide particular functionality, for example:

size = None
if isinstance(myvar, collections.Sized):
size = len(myvar)

This is not the case with Sequence ;简单地实现 Sequence 所需的所有方法并不能使您的类的实例通过 isinstance 检查。

原因是 Sequence类没有 __subclasshook__ ;如果不存在,则查询父类 __subclasshook__ ;在这种情况下 Sized.__subclasshook__ ;如果测试类不是完全 Sized,则返回 NotImplemented

另一方面,无法通过魔术方法区分映射类型和序列类型,因为它们都可以具有完全相同的魔术方法 - collections.OrderedDict 具有所有 Sequence 的神奇方法,包括 __reversed__ 方法,但它不是序列。

但是,您仍然不需要从 Sequence 继承来使 isinstance(Point, Sequence) 返回 True。在以下示例中,Point 是相同的,除了派生自 object 而不是 Sequence,在 Python 2 上:

>>> pt = Point(12, 42)
>>> pt.z = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> isinstance(pt, Sequence)
False
>>> Sequence.register(pt)
>>> isinstance(pt, Sequence)
True

您可以将任何类注册为抽象基类的子类,以用于isinstance 检查;在额外的混合方法中,你真的只需要实现 countindex;其他人的功能将由 Python 运行时填充。

关于python - 为什么 __slots__ 在从抽象基类继承时在 Python 2 和 3 中表现不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29444130/

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