Python: как наследование слотов в подклассах на самом деле работает?
на Python Data model reference section on slots есть список заметок по использованию __slots__
. Я полностью смущен 1-м и 6-м пунктами, потому что они, кажется, противоречат друг другу.
первый пункт:
- при наследовании от класса без
__slots__
на из этого класса всегда будет доступный, так что__slots__
определение в подклассе есть бессмысленный.
шестой пункт:
- действие a
__slots__
объявление ограничено классом где это определено. В результате, подклассы будут иметь__dict__
разве что они тоже определяют__slots__
(который должен содержать только имена любых дополнительный слот.)
мне кажется, что эти элементы могут быть лучше сформулированы или показаны через код, но я пытался обернуть голову вокруг этого и все еще поднимаюсь смущенный. Я действительно понимаю, как __slots__
are предполагается использовать, и я пытаюсь лучше понять, как они работают.
Вопрос:
может ли кто-нибудь объяснить мне простым языком, каковы условия наследования слотов при подклассах?
(простые примеры кода были бы полезны, но не нужны.)
5 ответов:
как уже упоминалось, единственная причина для определения
__slots__
это сохранить некоторую память, когда у вас есть простые объекты с предопределенным набором атрибутов и не хотите, чтобы каждый из них носил словарь. Это имеет смысл только для классов, которые вы планируете иметь много экземпляров, конечно.экономия может быть не сразу очевидна -- рассмотрим...:
>>> 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
из этого, казалось бы, размер с-слотов больше чем нет-размер слотов! Но это ошибка, потому что
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__
на задание атрибут, но не совсем, действительно крошечный СОПпреимущества для начало его).теперь, что касается наследования: для экземпляра, чтобы быть дикт-менее,все классы до его наследования цепочка также должна иметь экземпляры без диктанта. Классы с экземплярами без dict-это те, которые определяют
__slots__
, плюс большинство встроенных типов (встроенные типы, экземпляры которых имеют dicts,-это те, на экземплярах которых вы можете установить произвольные атрибуты, такие как функции). Перекрытия в именах слотов не запрещены, но они бесполезны и тратят некоторую память, так как слоты наследуются:>>> class A(object): __slots__='a' ... >>> class AB(A): __slots__='b' ... >>> ab=AB() >>> ab.a = ab.b = 23 >>>
как вы видите, вы можете установить атрибут
a
на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
так что на самом деле нет причин делать это.
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.
вам, вероятно, не нужно будет использовать
__slots__
в ближайшее время. Он предназначен только для экономии памяти за счет некоторой гибкости. Если у вас нет десятков тысяч объектов, это не будет иметь значения.
Python: как наследование
__slots__
в подклассах на самом деле работают?я полностью запутался в 1-м и 6-м пунктах, потому что они, кажется, противоречат друг другу.
эти пункты не противоречат друг другу. Первый касается подклассов классов, которые не реализуют
__slots__
, второй касается подклассов классов, которые do реализовать__slots__
.подклассы классов что не реализуют
__slots__
я все больше осознаю, что, как бы велики ни были документы Python (по праву), они не идеальны, особенно в отношении менее используемых функций языка. Я бы изменил docs следующим образом:
при наследовании от класса без
__slots__
на
из ответа, который вы связали:
правильное использование
__slots__
Это для экономии места в объектах. Вместо того, чтобы иметь динамический дикт..." при наследовании от класса без
__slots__
на__dict__
атрибут этого класса всегда будет доступен", поэтому добавьте свой собственный__slots__
нельзя запретить объектам иметь__dict__
, и не может сохранить космос.о
__slots__
не быть унаследованным немного тупо. Помните, что это magic attribute и не ведет себя как другие атрибуты, а затем перечитайте, что, как говорится, это поведение magic slots не наследуется. (Это действительно все.)
в моем понимании это выглядит следующим образом:
класс
X
нет__dict__
<------->
классX
и все его суперклассы имеют__slots__
указанов этом случае фактические слоты класса состоят из объединения
__slots__
объявленияX
и его суперклассы; поведение не определено (и станет ошибкой), если это объединение не является непересекающимся