2016-10-12 1 views
4
class Spam(object): 
    __slots__ = ('__dict__',) 

"일반"클래스보다 작은 인스턴스를 생성합니다. 왜 이런거야?왜 __slots__ = ('__dict__',) 작은 인스턴스를 생성합니까?

출처 : David Beazley's recent tweet.

+3

틀림없이 [__dir__이 (가) 없기 때문에] (https://twitter.com/TeemuRisikko/status/786202646994563077)입니다. 그런 일이 일어나는 이유는 무엇인지 모르겠습니다. –

+1

질문을 예제로 업데이트하는 것이 좋습니다. – Kasramvd

+3

"왜"라는 것이 꽤 명백합니다. "어떻게"라는 것은 파이썬이 객체 별 속성'dict'을 만드는 것을 건너 뜁니다. 속성 이름을'__dict__'라고 명명하는 것은 조금 이상합니다 ...'__init__'에서'self .__ dict__ = {}'를하면'__slots__'의 목적을 무효로합니다. – tdelaney

답변

7

저에게 메모리 절약 효과는 인스턴스에 __weakref__이없는 것으로 보입니다.

은 우리가 그래서 경우 : (파이썬 3.5.2)에

class Spam1(object): 
    __slots__ = ('__dict__',) 

class Spam2(object): 
    __slots__ = ('__dict__', '__weakref__') 

class Spam3(object): 
    __slots__ = ('foo',) 

class Eggs(object): 
    pass 

objs = Spam1(), Spam2(), Spam3(), Eggs() 
for obj in objs: 
    obj.foo = 'bar' 

import sys 
for obj in objs: 
    print(type(obj).__name__, sys.getsizeof(obj)) 

결과은 다음과 같습니다

Spam1 48 
Spam2 56 
Spam3 48 
Eggs 56 

우리는 볼 수는 (a __weakref__가있다) Eggs 같은 크기 Spam2 (전통적인 수업).

일반적으로 이렇게 절약하면 슬롯이 활성화 된 클래스에서 약한 참조를 사용할 수 없게됩니다. 일반적으로 __slots__의 절약액은 처음에는 __dict__을 만들지 않는다는 사실에서 비롯된 것입니다. __dict__은 다소 희소 한 테이블을 사용하여 구현되므로 (해시 충돌을 방지하고 O (1) 조회/삽입/삭제를 유지하기 위해) 프로그램에서 만드는 각 사전에 사용되지 않는 상당한 공간이 있습니다. 그래도 __slots__'__dict__'을 추가하면이 최적화를 놓칠 수 있습니다 (사전은 작성됩니다).

은 우리가 더 많은 슬롯을 추가 할 수 있습니다,이 조금 더 탐색하려면 : 우리가 다시 실행하면 이제

class Spam3(object): 
    __slots__ = ('foo', 'bar') 

을, 우리가 걸리는 것을 볼 수 :

Spam1 48 
Spam2 56 
Spam3 56 
Eggs 56 

그래서 각 슬롯은 8 바이트 소요 인스턴스 (나를 위해 - 아마도 8 바이트가 내 시스템에 sizeof(pointer)이기 때문에). 또한 __slots__은 설명 자 (인스턴스가 아닌 클래스에 있음)를 작성하여 구현됩니다. 따라서 __slots__dir(instance)을 통해 나열된 경우에도 실제로는 __slots__ 값을 가지고 있지 않습니다.) - 클래스으로 옮겨졌습니다.

이렇게하면 슬롯 사용 가능 클래스가 "기본"값을 설정할 수 없게됩니다. 예 : 다음 코드는 작동하지 않습니다

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

그래서 그것을 아래로 끓여야 :

  • 인스턴스의 각 "슬롯은"시스템에 대한 포인터의 크기를 차지합니다. __slots__ = ('__dict__',)__dict__ 슬롯 및 슬롯 __weakref__없이
  • 는 인스턴스와 __slots__ = ('__dict__',)
  • 에 생성되는 __dict__ 슬롯이 생성되지만 __weakref__ 슬롯은 인스턴스 생성되지 않는다.
  • 어쨌든 __slots__은 실제로 인스턴스 인에 적용되지 않습니다. 클래스에 있습니다 (dir(instance)에서 볼 수 있음).
  • 이런 식으로 __slots__을 사용하면 절약 할 수있는 효과는 미미합니다. 인스턴스에 dict을 만들지 않으면 (dict은 데이터 구조에서 다소 희박한 패킹 데이터로 인해 내용에 필요한 저장소의 합계보다 많은 저장소를 차지하므로) __slots__의 실제 절감액이 발생합니다. 그 중에서도 이러한 방법으로 슬롯을 사용하는 것이 단점이 있습니다 (예 : 인스턴스에 약한 참조 없음).
+1

다른 이유로는 부족한 부분이 있기 때문에 다른 곳에서는'__slots__'을 사용합니다. 차이점은'__slots__'과'__weakref__'의 크기 차이와 같아야하지만 그렇지 않습니다! – Kasramvd

+0

@ Kasramvd - 올바르게 이해하면'__slots__'는 _class_에 설명자를 생성하므로'__slots__'는 실제로 _instance_로 옮겨지지 않습니다. 그 인스턴스는 단지 메모리 슬롯 ('sizeof (pointer)')을 가지고 있습니다. 약한 참조와 함께,'getsifof'는'__weakref__'리스트의 크기에 약간의 오버 헤드를 추가합니다. - 추적하기는 꽤 어렵지만 'getsizeof'가 실제로 여러분에게보고하는 숫자를 알려주는 것입니다 :-) – mgilson

+0

네, 분명한 것은 여러분이 언급 한'__weakref__'과'__slots__' 때문에 차이가 있다는 것입니다. 그러나 차이점의 정확한 이유가 무엇인지 알아내는 것은 쉽지 않습니다. 특히 파이썬 수준. – Kasramvd

관련 문제