В справочном отделе модели данных Python на слотах существует список примечаний по использованию __slots__
. Я полностью смущен 1-ми и 6-ми объектами, потому что они, кажется, противоречат друг другу.
Первый объект:
__slots__
, __dict__
атрибут того класса всегда будет доступен, таким образом, a __slots__
определение в подклассе бессмысленно.Шестой объект:
__slots__
объявление ограничено классом, где это определяется. В результате подклассы будут иметь a __dict__
если они также не определяют __slots__
(который должен только содержать названия любых дополнительных слотов).Это кажется мне, эти объекты могли быть лучше сформулированы или показаны через код, но я пытался перенести голову вокруг этого, и все еще подхожу перепутанный. Я действительно понимаю как __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
Из этого может показаться, что размер 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
, поэтому на самом деле нет причин для этого.
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 __
- это экономия места в объектах. Вместо динамического dict ...
«При наследовании от класса без __ slots __
атрибут __ dict __
этого класса всегда будет доступен», поэтому добавьте свой собственный __ slots __
не может предотвратить объекты, имеющие __ dict __
, и не может сэкономить место.
Бит о __ slots __
, не наследуемых, немного тупой. Помните, что это магический атрибут и он не ведет себя как другие атрибуты, а затем перечитайте его, говоря, что это поведение волшебных слотов не наследуется. (Это действительно все.)
Я понимаю следующее:
class X
не имеет __ dict __
<------->
class X
и его суперклассы имеют __ slots __
, указанные
в этом случае фактические слоты класса состоят из объединения объявлений __ slots __
для X
и его суперклассы; поведение не определено (и станет ошибкой), если это объединение не является непересекающимся