Питон:Как на самом деле работает наследование __слотов__ в подклассах?

StackOverflow https://stackoverflow.com/questions/1816483

Вопрос

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

Первый пункт:

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

Шестой пункт:

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

Мне кажется, что эти элементы могли бы быть лучше сформулированы или показаны с помощью кода, но я пытался разобраться в этом и все еще нахожусь в замешательстве.Я действительно понимаю, как __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 утверждается, что они принимают во внимание не все, что задействовано в каждом конкретном случае.Вы можете лучше справляться со сторонними расширениями, такими как пимплер:

>>> 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 экземпляр -- 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__ в ближайшем будущем.Это предназначено только для экономии памяти ценой некоторой гибкости.Если у вас нет десятков тысяч объектов, это не будет иметь значения.

Питон:Как происходит наследование __slots__ в подклассах действительно работают?

Я совершенно сбит с толку 1-м и 6-м пунктами, потому что они, кажется, противоречат друг другу.

Эти пункты на самом деле не противоречат друг другу.Первый касается подклассов классов, которые не реализуют __slots__, второй касается подклассов классов , которые делай реализовать __slots__.

Подклассы классов, которые не реализуют __slots__

Я все больше осознаю, что какими бы замечательными ни считались документы Python (по праву), они не идеальны, особенно в том, что касается менее используемых функций языка.Я бы изменил Документы следующим образом:

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

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

Это изменение было принято и сейчас находится в последняя документация.

Вот пример:

class Foo: 
    """instances have __dict__"""

class Bar(Foo):
    __slots__ = 'foo', 'bar'

Bar в нем есть не только объявленные слоты, но и слоты Foo, которые включают __dict__:

>>> b = Bar()
>>> b.foo = 'foo'
>>> b.quux = 'quux'
>>> vars(b)
{'quux': 'quux'}
>>> b.foo
'foo'

Подклассы классов , которые делай реализовать __slots__

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

Ну, это тоже не совсем правильно.Действие a __slots__ декларация является не полностью ограничен классом, в котором он определен.Они могут иметь последствия, например, для множественного наследования.

Я бы изменил это на:

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

Я действительно обновил его, чтобы прочитать:

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

Вот пример:

class Foo:
    __slots__ = 'foo'

class Bar(Foo):
    """instances get __dict__ and __weakref__"""

И мы видим, что подкласс щелевого класса получает возможность использовать слоты:

>>> b = Bar()
>>> b.foo = 'foo'
>>> b.bar = 'bar'
>>> vars(b)
{'bar': 'bar'}
>>> b.foo
'foo'

(Подробнее о __slots__, смотрите мой ответ здесь.)

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

Правильное использование __slots__ заключается в экономии места в объектах.Вместо того, чтобы иметь динамический диктант...

"При наследовании от класса без __slots__, тот __dict__ атрибут этого класса всегда будет доступен", поэтому добавляйте свой собственный __slots__ не может запретить объектам иметь __dict__, и не может сэкономить место.

Немного о __slots__ не быть унаследованным - это немного глупо.Помните, что это магический атрибут и он ведет себя не так, как другие атрибуты, затем перечитайте его как говорящий о том, что это поведение magic slots не наследуется.(На самом деле это все, что от него требуется.)

Мое понимание заключается в следующем:

  • класс X не имеет __dict__ <-------> класс X и все его суперклассы имеют __slots__ указанный

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top