从抽象基类继承时,为什么__slots__在Python 2和3中的行为不同? [英] Why is __slots__ behaving differently in Python 2 and 3 when inheriting from an abstract base class

查看:130
本文介绍了从抽象基类继承时,为什么__slots__在Python 2和3中的行为不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I created the following class to store changeable points on a plane in a memory-efficient manner - I need a mutable equivalent of namedtuple('Point', 'x y'). Since instance dictionaries are big, I thought I'd go for __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上进行测试时,一切似乎都没问题:

When testing it on Python 3, everything seemed to be OK:

>>> 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:

However on Python 2, I can set the attribute z even when it is not in slots:

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

为什么会这样,为什么Python 2和Python 3之间会有区别?

Why is that so, and why the difference between Python 2 and Python 3?

推荐答案

Python 2数据模型在

The Python 2 data model says the following on __slots__:

  • 从没有__slots__的类继承时,该类的__dict__属性将始终可访问,因此子类中的__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__:

And that is what is happening here. In Python 2 the abstract base classes in the collections module did not have the __slots__ at all:

>>> 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__'

在CPython问题跟踪程序中,它被报告为 issue 11333 ,并已在Python 3.3中修复.

This was reported as the issue 11333 in CPython issue tracker, and was fixed in Python 3.3.

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

In Python 3.3+ the Sequence abstract base class now has __slots__ set to an empty tuple:

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


因此,在Python 2中,您不能从collections基类继承,并且不能同时与__slots__具有内存有效存储.


Thus in Python 2, you cannot inherit from a collections base class and have memory efficient storage with __slots__ at the same time.

但是请注意,即使 collections抽象基类的文档声称

Note however that even though the documentation on collections abstract base classes claims that

这些ABC允许我们询问类或实例是否提供特定功能,例如:

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)

Sequence 并非如此;简单地实现Sequence所需的所有方法并不能使您的类的实例通过isinstance检查.

This is not the case with Sequence; simply implementing all the methods required by the Sequence does not make instances of your class to pass the isinstance check.

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

The reason for that is that the Sequence class does not have a __subclasshook__; and in its absence, the parent class __subclasshook__ is consulted instead; in this case Sized.__subclasshook__; and that returns NotImplemented if the tested-against class wasn't exactly Sized.

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

On the other hand, one couldn't distinguish between a mapping type and a sequence type by the magic methods, as both of them can have the exactly same magic methods - of collections.OrderedDict has all the magic methods of a Sequence, including the __reversed__ method, yet it isn't a sequence.

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

However, you still do not need to inherit from Sequence to make isinstance(Point, Sequence) return True. In the following example, the Point is the same, except derived from object instead of Sequence, on 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运行时填充.

You can register any class as a subclass of the abstract base class for the purpose of isinstance checks; and of the extra mix-in methods, you really need to implement only count and index; the functionality for others will be filled in by Python runtime.

这篇关于从抽象基类继承时,为什么__slots__在Python 2和3中的行为不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆