Как делает наследование __, слоты __ в подклассах на самом деле работают?

В справочном отделе модели данных Python на слотах существует список примечаний по использованию __slots__. Я полностью смущен 1-ми и 6-ми объектами, потому что они, кажется, противоречат друг другу.

Первый объект:

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

Шестой объект:

  • Действие a __slots__ объявление ограничено классом, где это определяется. В результате подклассы будут иметь a __dict__ если они также не определяют __slots__ (который должен только содержать названия любых дополнительных слотов).

Это кажется мне, эти объекты могли быть лучше сформулированы или показаны через код, но я пытался перенести голову вокруг этого, и все еще подхожу перепутанный. Я действительно понимаю как __slots__ как предполагается, используются, и я пытаюсь надеть лучшее схватывание, как они работают.

Вопрос:

Кто-то может объяснить мне на простом языке, что условия для наследования слотов при разделении на подклассы?

(Простые примеры кода были бы полезны, но не необходимы.)

59
задан pppery 25 October 2019 в 20:49
поделиться

4 ответа

Как уже упоминалось другими, единственная причина для определения __ слотов __ - это сэкономить некоторую память, когда у вас есть простые объекты с предопределенным набором атрибутов и вы не хотите, чтобы каждый носить с собой словарь. Это, конечно, имеет смысл только для классов, экземпляров которых вы планируете иметь много.

Экономия может быть не сразу очевидна - подумайте ...:

>>> class NoSlots(object): pass
... 
>>> n = NoSlots()
>>> class WithSlots(object): __slots__ = 'a', 'b', 'c'
... 
>>> w = WithSlots()
>>> n.a = n.b = n.c = 23
>>> w.a = w.b = w.c = 23
>>> sys.getsizeof(n)
32
>>> sys.getsizeof(w)
36

Из этого может показаться, что размер with-slots составляет больше , чем размер без слотов! Но это ошибка, потому что sys.getsizeof не учитывает «содержимое объекта», такое как словарь:

>>> sys.getsizeof(n.__dict__)
140

Поскольку один dict занимает 140 байтов, очевидно, что объект «32 байта» n якобы принимает во внимание не все, что связано с каждым экземпляром. Вы можете лучше справиться со сторонними расширениями, такими как pympler :

>>> import pympler.asizeof
>>> pympler.asizeof.asizeof(w)
96
>>> pympler.asizeof.asizeof(n)
288

Это гораздо более четко показывает объем памяти, который сохраняется __ slots __ : для простого объекта, такого как этот в случае, это чуть меньше 200 байт, почти 2/3 от общей площади объекта. Теперь, поскольку в наши дни более или менее мегабайт на самом деле не имеет большого значения для большинства приложений, это также говорит вам, что __ slots __ не стоит беспокоиться, если вы собираетесь иметь всего несколько тысяч экземпляров одновременно - однако для миллионов экземпляров это действительно имеет очень важное значение. Вы также можете получить микроскопическое ускорение (отчасти из-за лучшего использования кеша для небольших объектов с __ slots __ ):

$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x'
10000000 loops, best of 3: 0.37 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x'
1000000 loops, best of 3: 0.604 usec per loop
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.28 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.332 usec per loop

, но это в некоторой степени зависит от версии Python (это числа, которые я измеряю повторно с помощью 2.5; с 2.6 я вижу большее относительное преимущество для __ slots __ для установки атрибута, но совсем ничего, действительно крошечное преимущество dis для получения it).

Теперь по поводу наследования: для экземпляра, который не требует dict, all классы вверх по цепочке наследования также должны иметь экземпляры без словаря. Классы с экземплярами без dict - это те, которые определяют __ slots __ , а также большинство встроенных типов (встроенные типы, экземпляры которых имеют dicts, - это те, для экземпляров которых вы можете устанавливать произвольные атрибуты, такие как функции). Перекрытия в названиях слотов не запрещены, но они бесполезны и тратят часть памяти, поскольку слоты наследуются:

>>> class A(object): __slots__='a'
... 
>>> class AB(A): __slots__='b'
... 
>>> ab=AB()
>>> ab.a = ab.b = 23
>>> 

как видите, вы можете установить атрибут a в экземпляре AB - AB сам определяет только слот b , но наследует слот a из A . Повторение унаследованного слота не запрещено:

>>> class ABRed(A): __slots__='a','b'
... 
>>> abr=ABRed()
>>> abr.a = abr.b = 23

, но тратит немного памяти:

>>> pympler.asizeof.asizeof(ab)
88
>>> pympler.asizeof.asizeof(abr)
96

, поэтому на самом деле нет причин для этого.

103
ответ дан 24 November 2019 в 18:17
поделиться
class WithSlots(object):
    __slots__ = "a_slot"

class NoSlots(object):       # This class has __dict__
    pass

Первый элемент

class A(NoSlots):            # even though A has __slots__, it inherits __dict__
    __slots__ = "a_slot"     # from NoSlots, therefore __slots__ has no effect

Шестой элемент

class B(WithSlots):          # This class has no __dict__
    __slots__ = "some_slot"

class C(WithSlots):          # This class has __dict__, because it doesn't
    pass                     # specify __slots__ even though the superclass does.

Вероятно, вам не нужно будет использовать __ слоты __ в ближайшем будущем. Он предназначен только для экономии памяти за счет некоторой гибкости. Если у вас нет десятков тысяч объектов, это не имеет значения.

13
ответ дан 24 November 2019 в 18:17
поделиться

Из ответа, который вы связали:

Правильное использование __ slots __ - это экономия места в объектах. Вместо динамического dict ...

«При наследовании от класса без __ slots __ атрибут __ dict __ этого класса всегда будет доступен», поэтому добавьте свой собственный __ slots __ не может предотвратить объекты, имеющие __ dict __ , и не может сэкономить место.

Бит о __ slots __ , не наследуемых, немного тупой. Помните, что это магический атрибут и он не ведет себя как другие атрибуты, а затем перечитайте его, говоря, что это поведение волшебных слотов не наследуется. (Это действительно все.)

2
ответ дан 24 November 2019 в 18:17
поделиться

Я понимаю следующее:

  • class X не имеет __ dict __ <-------> class X и его суперклассы имеют __ slots __ , указанные

  • в этом случае фактические слоты класса состоят из объединения объявлений __ slots __ для X и его суперклассы; поведение не определено (и станет ошибкой), если это объединение не является непересекающимся

1
ответ дан 24 November 2019 в 18:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: