Program Club

Python : 하위 클래스에서 __slots__ 상속은 실제로 어떻게 작동합니까?

proclub 2020. 12. 7. 21:06
반응형

Python : 하위 클래스에서 __slots__ 상속은 실제로 어떻게 작동합니까?


에서 슬롯에 파이썬 데이터 모델의 참조 섹션 사용에 대한주의 사항의 목록이 있습니다 __slots__. 1 번과 6 번 항목이 서로 모순되는 것 같아 완전히 혼란 스럽습니다.

첫 번째 항목 :

  • 없는 클래스에서 상속하면 해당 클래스 __slots____dict__속성에 항상 액세스 할 수 있으므로 __slots__하위 클래스 정의는 의미가 없습니다.

여섯 번째 항목 :

  • __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

이로부터 슬롯이없는 크기가 슬롯이없는 크기 보다 큰 것처럼 보입니다 ! 그러나 이는 sys.getsizeof사전과 같은 "객체 내용"을 고려하지 않기 때문에 실수입니다 .

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

딕셔너리만으로 140 바이트를 차지하기 때문에 "32 바이트"객체 n는 각 인스턴스에 관련된 모든 것을 고려하지 않는다고합니다. pympler 와 같은 타사 확장 프로그램으로 더 나은 작업을 수행 할 수 있습니다 .

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

이것은에 의해 절약 된 메모리 공간을 훨씬 더 명확하게 보여줍니다 __slots__.이 경우와 같은 간단한 객체의 경우, 객체 전체 공간의 거의 2/3 인 200 바이트보다 약간 작습니다. 요즘에는 메가 바이트 정도가 대부분의 응용 프로그램에서 그다지 중요하지 않기 때문에 __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

그러나 이것은 파이썬 버전에 다소 의존한다 (이 내가 2.5 반복적으로 측정하는 숫자이다; 2.6, 나는에 큰 비교 우위를 참조 __slots__하기위한 설정 속성을하지만 전혀 없음, 실제로 작은 DIS에 대한 장점, 점점 그것을).

이제 상속과 관련하여 인스턴스 가 dict-less가 되려면 상속 체인의 모든 클래스에도 dict-less 인스턴스가 있어야합니다. dict-less 인스턴스가있는 클래스는를 정의하는 클래스와 __slots__대부분의 내장 유형 ( 인스턴스에 dict가있는 내장 유형은 함수와 같은 임의의 속성을 설정할 수있는 인스턴스의 인스턴스입니다)입니다. 슬롯 이름의 겹침은 금지되지 않지만 슬롯이 상속되기 때문에 쓸모없고 일부 메모리를 낭비합니다.

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

보시 다시피 인스턴스에 속성 a설정할 수 있습니다. 자체적으로 slot을 정의 하지만 에서 슬롯 상속합니다 . 상속 된 슬롯을 반복하는 것은 금지되지 않습니다.ABABbaA

>>> 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__in subclasses의 상속은 실제로 어떻게 작동합니까?

1 번과 6 번 항목이 서로 모순되는 것 같아 완전히 혼란 스럽습니다.

Those items don't actually contradict each other. The first regards subclasses of classes that don't implement __slots__, the second regards subclasses of classes that do implement __slots__.

Subclasses of classes that don't implement __slots__

I am increasingly aware that as great as the Python docs are (rightly) reputed to be, they are not perfect, especially regarding the less used features of the language. I would alter the docs as follows:

When inheriting from a class without __slots__, the __dict__ attribute of that class will always be accessible , so a __slots__ definition in the subclass is meaningless .

__slots__ is still meaningful for such a class. It documents the expected names of attributes of the class. It also creates slots for those attributes - they will get the faster lookups and use less space. It just allows for other attributes, which will be assigned to the __dict__.

This change has been accepted and is now in the latest documentation.

Here's an example:

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

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

Bar not only has the slots it declares, it also has Foo's slots - which include __dict__:

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

Subclasses of classes that do implement __slots__

The action of a __slots__ declaration is limited to the class where it is defined. As a result, subclasses will have a __dict__ unless they also define __slots__ (which must only contain names of any additional slots).

Well that's not quite right either. The action of a __slots__ declaration is not entirely limited to the class where it is defined. They can have implications for multiple inheritance, for example.

I would change that to:

For classes in an inheritance tree that defines __slots__, subclasses will have a __dict__ unless they also define __slots__ (which must only contain names of any additional slots).

I have actually updated it to read:

The action of a __slots__ declaration is not limited to the class where it is defined. __slots__ declared in parents are available in child classes. However, child subclasses will get a __dict__ and __weakref__ unless they also define __slots__ (which should only contain names of any additional slots).

Here's an example:

class Foo:
    __slots__ = 'foo'

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

And we see that a subclass of a slotted class gets to use the slots:

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

(For more on __slots__, see my answer here.)


From the answer you linked:

The proper use of __slots__ is to save space in objects. Instead of having a dynamic dict...

"When inheriting from a class without __slots__, the __dict__ attribute of that class will always be accessible", so adding your own __slots__ cannot prevent objects from having a __dict__, and cannot save space.

The bit about __slots__ not being inherited is a little obtuse. Remember that it's a magic attribute and doesn't behave like other attributes, then re-read that as saying this magic slots behavior isn't inherited. (That's really all there is to it.)


My understanding is as follows:

  • class X has no __dict__ <-------> class X and its superclasses all have __slots__ specified

  • in this case, the actual slots of the class are comprised from the union of __slots__ declarations for X and its superclasses; the behavior is undefined (and will become an error) if this union is not disjoint

참고URL : https://stackoverflow.com/questions/1816483/python-how-does-inheritance-of-slots-in-subclasses-actually-work

반응형