Почему __slots__ ведет себя по-другому в Python 2 и 3 при наследовании от абстрактного базового класса

Я создал следующий класс для хранения сменных точек на плоскости эффективным с точки зрения памяти образом – мне нужен изменяемый эквивалент 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?

One Solution collect form web for “Почему __slots__ ведет себя по-другому в Python 2 и 3 при наследовании от абстрактного базового класса”

В модели данных Python 2 указано следующее: __slots__ :

  • При наследовании от класса без __slots__ атрибут __dict__ этого класса всегда будет доступен, поэтому определение __slots__ в подклассе бессмысленно.

И вот что здесь происходит. В 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__' 

Об этом сообщается в выпуске 11333 в журнале отслеживания проблем CPython и исправлена ​​в Python 3.3.

В Python 3.3+ базовый класс Sequence теперь имеет __slots__ установленный в пустой кортеж:

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

Таким образом, в Python 2 вы не можете наследовать из базового класса collections и одновременно иметь хранилище с __slots__ .


Обратите внимание, однако, что хотя документация по collections абстрактных базовых классов утверждает, что

Эти ABC позволяют нам задавать классы или экземпляры, если они обеспечивают определенную функциональность, например:

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

Это не относится к Sequence ; простое выполнение всех методов, требуемых Sequence , не делает экземпляры вашего класса для передачи проверки isinstance .

Причина этого в том, что класс Sequence не имеет __subclasshook__ ; и при его отсутствии рассматривается родительский класс __subclasshook__ ; в этом случае Sized.__subclasshook__ ; и который возвращает NotImplemented если класс с 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 ; и дополнительных методов смешивания, вам действительно нужно реализовать только count и index ; функциональность для других будет заполнена временем выполнения Python.

  • Могу ли я принудительно отлаживать python на AssertionError?
  • Ошибка установки пакета: невозможно импортировать имя 'unpack_url'
  • python3 восстанавливает исключение с помощью настраиваемого атрибута?
  • Чтение и печать файлов Python с исключениями и окончаниями
  • Может ли кто-нибудь помочь мне понять это сито сценария Эратосфена? Последние пару строк меня озадачили
  • Как проверить корреляцию с использованием десятичных чисел / данных с помощью python 3
  • Свойство Python в списке
  • Вложенные классы python
  • Python - лучший язык программирования в мире.