답변:
파이썬에서, 이것의 목적은
__slots__
무엇이며 이것을 피해야 할 경우는 무엇입니까?
특수 속성을 __slots__
사용하면 객체 인스턴스에있을 것으로 예상되는 인스턴스 속성을 예상 결과와 함께 명시 적으로 지정할 수 있습니다.
공간 절약은
__dict__
.__dict__
및 __weakref__
생성 __slots__
.작은 경고, 상속 트리에서 특정 슬롯을 한 번만 선언해야합니다. 예를 들면 다음과 같습니다.
class Base:
__slots__ = 'foo', 'bar'
class Right(Base):
__slots__ = 'baz',
class Wrong(Base):
__slots__ = 'foo', 'bar', 'baz' # redundant foo and bar
파이썬은 당신이 이것을 잘못 받았을 때 반대하지 않으며 (아마도 그렇게해야합니다) 문제가 다르게 나타나지 않을 수도 있지만 객체가 그렇지 않은 경우보다 더 많은 공간을 차지합니다. 파이썬 3.8 :
>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(56, 72)
베이스의 슬롯 디스크립터에 잘못된 슬롯과 별도의 슬롯이 있기 때문입니다. 이것은 일반적으로 나타나지 않아야하지만 다음과 같이 할 수 있습니다.
>>> w = Wrong()
>>> w.foo = 'foo'
>>> Base.foo.__get__(w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: foo
>>> Wrong.foo.__get__(w)
'foo'
가장 큰 경고는 다중 상속에 대한 것입니다. 비어 있지 않은 슬롯이있는 여러 상위 클래스는 결합 할 수 없습니다.
이 제한을 수용하려면 모범 사례를 따르십시오. 구체적인 클래스와 새 콘크리트 클래스가 집합 적으로 상속 할 부모의 추상화를 제외하고 모두 추상화하십시오. 표준 라이브러리).
예제는 아래의 다중 상속 섹션을 참조하십시오.
에 지정된 속성 __slots__
이 실제로가 아닌 슬롯에 저장되도록 하려면 __dict__
클래스가에서 상속되어야합니다 object
.
의 생성을 막으려면 __dict__
상속을 object
상속 받아야하며 상속의 모든 클래스는 선언해야 __slots__
하며 그 중 어느 것도 '__dict__'
입력 할 수 없습니다 .
계속 읽으려면 자세한 내용이 많이 있습니다.
__slots__
: 더 빠른 속성 액세스.Python의 제작자 인 Guido van Rossum은 실제로 더 빠른 속성 액세스를 위해 생성했다고 말합니다__slots__
.
상당히 빠른 액세스 속도를 나타내는 것은 쉬운 일이 아닙니다.
import timeit
class Foo(object): __slots__ = 'foo',
class Bar(object): pass
slotted = Foo()
not_slotted = Bar()
def get_set_delete_fn(obj):
def get_set_delete():
obj.foo = 'foo'
obj.foo
del obj.foo
return get_set_delete
과
>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
Ubuntu의 Python 3.5에서는 슬롯 액세스가 거의 30 % 빠릅니다.
>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342
Windows의 Python 2에서는 약 15 % 더 빠릅니다.
__slots__
: 메모리 절약또 다른 목적은 __slots__
각 객체 인스턴스가 차지하는 메모리 공간을 줄이는 것입니다.
문서에 대한 본인의 공헌은 다음과 같은 이유를 분명히 나타냅니다 .
사용하여 절약 된 공간
__dict__
이 상당 할 수 있습니다.
SQLAlchemy 는 많은 메모리 절약 효과를 제공 __slots__
합니다.
이를 확인하기 위해 Ubuntu Linux에서 Python 2.7의 Anaconda 배포판을 사용하면 선언 되지 않은 클래스 인스턴스의 크기 ( guppy.hpy
와 힙이 있음) 는 64 바이트입니다. 포함 되지 않습니다 . 게으른 평가를 다시 한 번 감사드립니다. 참조 될 때까지 존재하지는 않지만 분명히 데이터가없는 클래스는 쓸모가 없습니다. 존재로 호출되면 속성은 추가로 최소 280 바이트입니다.sys.getsizeof
__slots__
__dict__
__dict__
__dict__
반대로 (데이터 없음) 으로 __slots__
선언 된 클래스 인스턴스 ()
는 16 바이트에 불과하며 슬롯에 하나의 항목이있는 총 56 바이트, 2가있는 64 개입니다.
64 비트 파이썬, 내가 파이썬 2.7와 3.6 바이트의 메모리 소비 도시 __slots__
하고 __dict__
딕셔너리 (0, 1, 2 개 속성 제외) 3.6에서 성장 각 점에 대해 (어떤 슬롯이 정의되지 않은)를 :
Python 2.7 Python 3.6
attrs __slots__ __dict__* __slots__ __dict__* | *(no slots defined)
none 16 56 + 272† 16 56 + 112† | †if __dict__ referenced
one 48 56 + 272 48 56 + 112
two 56 56 + 272 56 56 + 112
six 88 56 + 1040 88 56 + 152
11 128 56 + 1040 128 56 + 240
22 216 56 + 3344 216 56 + 408
43 384 56 + 3344 384 56 + 752
따라서 Python 3의 작은 dicts에도 불구하고 __slots__
메모리를 절약하기 위해 인스턴스를 얼마나 잘 확장 할 수 있는지 알 수 있습니다 __slots__
. 이것이 주요한 이유 입니다.
내 노트의 완성을 위해, 파이썬 2에서는 클래스 네임 스페이스에 64 바이트, 파이썬 3에는 72 바이트의 슬롯 당 1 회 비용이 발생합니다. 슬롯은 "멤버"라고하는 속성과 같은 데이터 디스크립터를 사용하기 때문입니다.
>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72
__slots__
:의 생성을 거부하려면 __dict__
서브 클래스를 작성 해야합니다 object
.
class Base(object):
__slots__ = ()
지금:
>>> b = Base()
>>> b.a = 'a'
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
b.a = 'a'
AttributeError: 'Base' object has no attribute 'a'
또는 다음을 정의하는 다른 클래스를 서브 클래스 화하십시오. __slots__
class Child(Base):
__slots__ = ('a',)
그리고 지금:
c = Child()
c.a = 'a'
그러나:
>>> c.b = 'b'
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
c.b = 'b'
AttributeError: 'Child' object has no attribute 'b'
__dict__
슬롯 객체를 서브 클래 싱하는 동안 생성 을 허용하려면 다음을 추가 '__dict__'
하십시오 __slots__
(슬롯이 정렬되어 이미 상위 클래스에있는 슬롯을 반복해서는 안 됨).
class SlottedWithDict(Child):
__slots__ = ('__dict__', 'b')
swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'
과
>>> swd.__dict__
{'c': 'c'}
또는 __slots__
서브 클래스에서 선언 할 필요조차 없으며 여전히 부모의 슬롯을 사용하지만 __dict__
: 생성을 제한하지는 않습니다 .
class NoSlots(Child): pass
ns = NoSlots()
ns.a = 'a'
ns.b = 'b'
과:
>>> ns.__dict__
{'b': 'b'}
그러나 __slots__
다중 상속에 문제가 발생할 수 있습니다.
class BaseA(object):
__slots__ = ('a',)
class BaseB(object):
__slots__ = ('b',)
비어 있지 않은 슬롯이 모두있는 부모에서 자식 클래스를 만드는 데 실패하기 때문에 :
>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
이 문제가 발생하는 경우, 당신은 수 만 제거 __slots__
부모로부터, 또는 부모의 컨트롤이있는 경우,이를 추상화에 슬롯, 또는 리팩토링을 비워 줄 :
from abc import ABC
class AbstractA(ABC):
__slots__ = ()
class BaseA(AbstractA):
__slots__ = ('a',)
class AbstractB(ABC):
__slots__ = ()
class BaseB(AbstractB):
__slots__ = ('b',)
class Child(AbstractA, AbstractB):
__slots__ = ('a', 'b')
c = Child() # no problem!
'__dict__'
하는 __slots__
동적 할당을 얻을 :class Foo(object):
__slots__ = 'bar', 'baz', '__dict__'
그리고 지금:
>>> foo = Foo()
>>> foo.boink = 'boink'
따라서 '__dict__'
슬롯에서 우리는 동적 할당이 있고 여전히 우리가 기대하는 이름에 대한 슬롯이 있다는 점에서 크기 이점의 일부를 잃습니다.
슬롯이 지정되지 않은 객체에서 상속하면 슬롯 값 __slots__
을 __slots__
가리키는 이름을 사용할 때와 동일한 종류의 시맨틱을 얻게 되지만 다른 값은 인스턴스의에 배치 __dict__
됩니다.
__slots__
속성을 즉시 추가 할 수 있기를 피하는 것은 실제로 좋은 이유가 아닙니다 . 필요한 경우 추가 "__dict__"
하십시오 __slots__
.
해당 기능이 필요한 경우 비슷하게 명시 적으로 추가 __weakref__
할 수 있습니다 __slots__
.
명명 된 튜플 내장은 매우 가벼운 불변 인스턴스 (실제로 튜플의 크기)를 만들지 만 이점을 얻으려면 서브 클래스로 만들면 직접해야합니다.
from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
"""MyNT is an immutable and lightweight object"""
__slots__ = ()
용법:
>>> nt = MyNT('bar', 'baz')
>>> nt.bar
'bar'
>>> nt.baz
'baz'
그리고 예기치 않은 속성을 할당하려고하면 다음과 AttributeError
같은 생성을 막았 기 때문에 발생합니다 __dict__
.
>>> nt.quux = 'quux'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyNT' object has no attribute 'quux'
당신은 할 수 있도록 __dict__
오프 남겨 생성을 __slots__ = ()
,하지만 당신은 비어 있지 않은 사용할 수 없습니다 __slots__
튜플의 하위 유형.
비어 있지 않은 슬롯이 여러 부모에 대해 동일한 경우에도 함께 사용할 수 없습니다.
class Foo(object):
__slots__ = 'foo', 'bar'
class Bar(object):
__slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()
>>> class Baz(Foo, Bar): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
__slots__
부모에서 공란 을 사용하면 유연성이 가장 높아져서 자식'__dict__'
이 동적 생성 __dict__
을 방지하거나 허용하도록 추가 할 수 있습니다 (위 섹션 참조) .
class Foo(object): __slots__ = ()
class Bar(object): __slots__ = ()
class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
b = Baz()
b.foo, b.bar = 'foo', 'bar'
슬롯 이 필요하지 않으므로 슬롯을 추가하고 나중에 제거해도 아무런 문제가 발생하지 않습니다.
여기에서 사지로 나가기 : 인스턴스화하려는 의도가 아닌 믹스 인을 작성 하거나 추상 기본 클래스를 사용 하는 경우 __slots__
해당 부모 의 빈 클래스는 하위 클래스의 유연성 측면에서 가장 좋은 방법으로 보입니다.
먼저 다중 상속에서 사용하려는 코드로 클래스를 작성해 보겠습니다.
class AbstractBase:
__slots__ = ()
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
예상 슬롯을 상속하고 선언하여 위의 내용을 직접 사용할 수 있습니다.
class Foo(AbstractBase):
__slots__ = 'a', 'b'
그러나 우리는 그것에 대해 신경 쓰지 않습니다. 그것은 사소한 단일 상속이므로 시끄러운 속성을 가진 또 다른 클래스가 필요합니다.
class AbstractBaseC:
__slots__ = ()
@property
def c(self):
print('getting c!')
return self._c
@c.setter
def c(self, arg):
print('setting c!')
self._c = arg
이제 두베이스에 비어 있지 않은 슬롯이 있으면 아래 작업을 수행 할 수 없습니다. (실제로 원한다면 AbstractBase
비어 있지 않은 슬롯 a와 b를 줄 수 있고 아래 선언에서 제외시킬 수 있습니다.
class Concretion(AbstractBase, AbstractBaseC):
__slots__ = 'a b _c'.split()
이제 다중 상속을 통해 기능을 사용할 수 있으며 여전히 거부 __dict__
하고 __weakref__
인스턴스화 할 수 있습니다 .
>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'
__class__
슬롯 레이아웃이 동일하지 않으면 클래스가없는 다른 클래스에 할당 을 수행 하거나 추가 할 수없는 경우에는 피하십시오. (누가이 일을하는지, 왜 배우는 지에 관심이 있습니다.)내가 최근에 많은 기여를 한 __slots__
문서 의 나머지 부분 (3.7 dev 문서가 최신 버전 임) 에서 추가 경고를 해결할 수 있습니다 .
현재의 최고 답변은 오래된 정보를 인용하고 손을 많이 들며 몇 가지 중요한 방법으로 마크를 놓칩니다.
__slots__
많은 객체를 인스턴스화 할 때만 사용하지 마십시오 "나는 인용한다 :
"
__slots__
동일한 클래스의 객체 (수백, 수천)를 인스턴스화 하려는 경우 사용하고 싶습니다 ."
예를 들어, collections
모듈의 추상 기본 클래스 는 인스턴스화되지 않았지만 __slots__
선언되어 있습니다.
왜?
사용자가 거부 __dict__
하거나 __weakref__
만들려면 부모 클래스에서 해당 항목을 사용할 수 없어야합니다.
__slots__
인터페이스 또는 믹스 인을 만들 때 재사용성에 기여합니다.
많은 파이썬 사용자가 재사용 성을 위해 글을 쓰지 않는 것이 사실이지만, 필요한 경우 불필요한 공간 사용을 거부 할 수있는 옵션을 갖는 것이 중요합니다.
__slots__
산세를 끊지 않습니다슬롯이있는 객체를 피클 링하면 오해의 소지가 있음을 알 수 있습니다 TypeError
.
>>> pickle.loads(pickle.dumps(f))
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
이것은 실제로 잘못되었습니다. 이 메시지는 가장 오래된 프로토콜 인 기본값입니다. -1
인수를 사용 하여 최신 프로토콜을 선택할 수 있습니다 . 파이썬 2.7에서는 이것이 2
2.3에 도입되었으며, 3.6에서는입니다 4
.
>>> pickle.loads(pickle.dumps(f, -1))
<__main__.Foo object at 0x1129C770>
파이썬 2.7에서 :
>>> pickle.loads(pickle.dumps(f, 2))
<__main__.Foo object at 0x1129C770>
파이썬 3.6에서
>>> pickle.loads(pickle.dumps(f, 4))
<__main__.Foo object at 0x1129C770>
그래서 나는 이것이 해결 된 문제이므로 이것을 명심해야합니다.
첫 번째 단락은 반 짧은 설명, 반 예측입니다. 실제로 질문에 대답하는 유일한 부분은 다음과 같습니다.
__slots__
객체의 공간을 절약 하는 것이 적절하게 사용 됩니다. 언제라도 객체에 속성을 추가 할 수있는 동적 dict 대신, 생성 후 추가를 허용하지 않는 정적 구조가 있습니다. 이것은 슬롯을 사용하는 모든 객체에 대해 하나의 받아쓰기의 오버 헤드를 절약합니다
후반은 희망적인 생각이며, 표시에서 벗어납니다.
이것은 때때로 유용한 최적화이지만, 파이썬 인터프리터가 동적으로 충분히 객체가 아니라면 실제로 객체에 추가가있을 때만 필요한 경우에는 완전히 불필요합니다.
파이썬은 실제로 이와 비슷한 것을 수행하여 __dict__
액세스 할 때만 생성 하지만 데이터가없는 많은 객체를 생성하는 것은 상당히 어리 석습니다.
두 번째 단락은 피해야 할 실제 이유를 지나치게 단순화하고 그리워 __slots__
합니다. 아래는 슬롯을 피하는 실제 이유 가 아닙니다 ( 실제 이유로 위의 나머지 답변을 참조하십시오).
그들은 제어 괴물과 정적 타이핑 이유에 의해 남용 될 수있는 방식으로 슬롯이있는 객체의 동작을 변경합니다.
그런 다음 파이썬과 관련이없는 목표를 달성하는 다른 방법에 대해 논의하고 관련이 없습니다. __slots__
.
세 번째 단락은 더 희망적인 생각입니다. 응답자가 사이트를 비판하는 탄약에 대한 글을 쓰지 않고 기여한 것은 대부분 비표준 콘텐츠입니다.
일반 객체와 슬롯 객체를 만듭니다.
>>> class Foo(object): pass
>>> class Bar(object): __slots__ = ()
그중 백만을 인스턴스화하십시오.
>>> foos = [Foo() for f in xrange(1000000)]
>>> bars = [Bar() for b in xrange(1000000)]
검사 guppy.hpy().heap()
:
>>> guppy.hpy().heap()
Partition of a set of 2028259 objects. Total size = 99763360 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 49 64000000 64 64000000 64 __main__.Foo
1 169 0 16281480 16 80281480 80 list
2 1000000 49 16000000 16 96281480 97 __main__.Bar
3 12284 1 987472 1 97268952 97 str
...
일반 객체에 액세스하여 __dict__
다시 검사하십시오.
>>> for f in foos:
... f.__dict__
>>> guppy.hpy().heap()
Partition of a set of 3028258 objects. Total size = 379763480 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 33 280000000 74 280000000 74 dict of __main__.Foo
1 1000000 33 64000000 17 344000000 91 __main__.Foo
2 169 0 16281480 4 360281480 95 list
3 1000000 33 16000000 4 376281480 99 __main__.Bar
4 12284 0 987472 0 377268952 99 str
...
이것은 파이썬 2.2의 유형과 클래스 통합 에서 파이썬의 역사와 일치합니다
당신이 서브 클래스 경우 내장 타입, 여분의 공간이 자동으로 수용하는 경우에 추가
__dict__
하고__weakrefs__
. (__dict__
사용할 때까지는 초기화되지 않으므로 생성하는 각 인스턴스에 대해 빈 사전이 차지하는 공간에 대해 걱정하지 않아도됩니다.)이 추가 공간이 필요하지 않은 경우 "__slots__ = []
" 라는 문구를 추가 할 수 있습니다. 수업.
__slots__
. 진심으로! 감사합니다!
__slots__
약 1 년 전에 파이썬 문서에 기여했습니다 : github.com/python/cpython/pull/1819/files
인용 제이콥 할렌 :
__slots__
객체의 공간을 절약 하는 것이 적절하게 사용 됩니다. 언제든 객체에 속성을 추가 할 수있는 동적 dict 대신, 생성 후 추가를 허용하지 않는 정적 구조가 있습니다. [이러한 사용은__slots__
모든 객체에 대해 하나의 dict의 오버 헤드 를 제거합니다.] 이것은 때때로 유용한 최적화이지만, 파이썬 인터프리터가 충분히 동적 인 경우 실제로 불필요합니다. 목적.불행히도 슬롯에는 부작용이 있습니다. 그들은 제어 괴물과 정적 타이핑 이유에 의해 남용 될 수있는 방식으로 슬롯이있는 객체의 동작을 변경합니다. 컨트롤 괴물이 메타 클래스를 학대하고 정적 타이핑 이유가 데코레이터를 학대해야하기 때문에 이것은 나쁘다. 파이썬에서는 무언가를하는 명백한 방법이 하나 밖에 없기 때문이다.
CPython이 공간 절약을 처리 할 수있을만큼 똑똑하게 만드는
__slots__
것은 중요한 작업이므로 P3k (아직) 변경 목록에없는 이유 일 수 있습니다.
__slots__
정적 타이핑과 동일한 문제를 해결하지 못합니다. 예를 들어 C ++에서는 멤버 변수 선언이 제한되지 않고 의도하지 않은 유형 (및 컴파일러 적용)이 해당 변수에 할당됩니다. 의 사용을 용납하지 않고 __slots__
대화에 관심이 있습니다. 감사!
__slots__
같은 클래스의 많은 객체 (수백, 수천)를 인스턴스화 하려는 경우 에 사용 하려고합니다. __slots__
메모리 최적화 도구로만 존재합니다.
__slots__
속성 생성을 제한하는 데 사용하지 않는 것이 좋습니다 .
__slots__
피클 링 객체 는 기본 (가장 오래된) 피클 프로토콜에서 작동하지 않습니다. 이후 버전을 지정해야합니다.
파이썬의 일부 다른 내부 검사 기능도 악영향을받을 수 있습니다.
각 파이썬 객체에는 __dict__
다른 모든 속성을 포함하는 사전 인 속성이 있습니다. 예를 들어 self.attr
python 을 입력하면 실제로 수행 self.__dict__['attr']
됩니다. 사전을 사용하여 속성을 저장한다고 가정하면 속성에 액세스하는 데 약간의 공간과 시간이 필요합니다.
그러나를 사용하면 __slots__
해당 클래스에 대해 생성 된 객체에는 __dict__
속성 이 없습니다 . 대신, 모든 속성 액세스는 포인터를 통해 직접 수행됩니다.
따라서 본격적인 클래스가 아닌 C 스타일 구조를 원한다면 __slots__
객체의 크기를 줄이고 속성 액세스 시간을 줄이는 데 사용할 수 있습니다 . 좋은 예는 x & y 속성을 포함하는 Point 클래스입니다. 당신이 많은 포인트를 가질 예정이라면, 당신은 __slots__
약간의 메모리를 절약하기 위해 사용해 볼 수 있습니다 .
__slots__
정의는 하지 C 스타일의 구조처럼. 클래스 수준 사전 매핑 속성 이름을 인덱스에 매핑하면 다음과 같은 결과를 얻을 수 없습니다. class A(object): __slots__= "value",\n\na=A(); setattr(a, 'value', 1)
이 답변을 명확히해야한다고 생각합니다 (원하는 경우 수행 할 수 있음). 또한 instance.__hidden_attributes[instance.__class__[attrname]]
그보다 빠르지 않습니다 instance.__dict__[attrname]
.
다른 답변 외에도 다음을 사용하는 예가 있습니다 __slots__
.
>>> class Test(object): #Must be new-style class!
... __slots__ = ['x', 'y']
...
>>> pt = Test()
>>> dir(pt)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__slots__', '__str__', 'x', 'y']
>>> pt.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> pt.x = 1
>>> pt.x
1
>>> pt.z = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'z'
>>> pt.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__dict__'
>>> pt.__slots__
['x', 'y']
따라서을 구현 __slots__
하려면 추가 줄이 필요합니다 (아직 클래스가 아닌 경우 클래스를 새 스타일 클래스로 만들기). 이 방법을 사용하면 필요할 때 사용자 지정 피클 코드를 작성하지 않고도 해당 클래스의 메모리 사용 공간을 5 배 줄일 수 있습니다 .
클래스 인스턴스의 속성에는 인스턴스, 속성 이름 및 속성 값의 3 가지 속성이 있습니다.
에서 일반 속성 액세스 , 인스턴스는 사전의 역할을하고 사전에 키 값을 찾는으로 속성의 이름이 역할을합니다.
인스턴스 (속성)-> 값
에서 __slots__ 액세스 , 속성의 이름은 사전 역할을하며 사전에 키 값을 찾는대로 인스턴스는 역할을합니다.
속성 (인스턴스)-> 값
에서 플라이급 패턴 , 속성의 이름은 사전 역할을하며 값은 인스턴스를 찾는 것을 사전에 핵심 역할을합니다.
속성 (값)-> 인스턴스
__slots__
합니까? "
매우 간단한 __slot__
속성 예입니다 .
__slots__
__slot__
클래스에 속성 이 없으면 객체에 새 속성을 추가 할 수 있습니다.
class Test:
pass
obj1=Test()
obj2=Test()
print(obj1.__dict__) #--> {}
obj1.x=12
print(obj1.__dict__) # --> {'x': 12}
obj1.y=20
print(obj1.__dict__) # --> {'x': 12, 'y': 20}
obj2.x=99
print(obj2.__dict__) # --> {'x': 99}
위의 예를 보면 obj1 및 obj2에 고유 한 x 및 y 속성이 있으며 python이 dict
각 객체 ( obj1 및 obj2 )에 대한 속성 도 생성 했음을 알 수 있습니다 .
내 클래스 Test 에 수천 개의 이러한 객체가 있는지 가정하십시오 . dict
각 객체에 대한 추가 속성 을 만들면 내 코드에 많은 오버 헤드 (메모리, 컴퓨팅 성능 등)가 발생합니다.
__slots__
이제 다음 예제에서 내 클래스 Test 는 __slots__
속성을 포함 합니다. 이제 객체에 새 속성을 추가 할 수 없으며 (attribute 제외 x
) 파이썬은 dict
더 이상 속성을 만들지 않습니다. 이렇게하면 각 개체의 오버 헤드가 제거되어 개체가 많을 경우 중요해질 수 있습니다.
class Test:
__slots__=("x")
obj1=Test()
obj2=Test()
obj1.x=12
print(obj1.x) # --> 12
obj2.x=99
print(obj2.x) # --> 99
obj1.y=28
print(obj1.y) # --> AttributeError: 'Test' object has no attribute 'y'
다소 애매 모호한 또 다른 용도는 __slots__
이전 PEAK 프로젝트의 일부인 ProxyTypes 패키지에서 객체 프록시에 속성을 추가하는 것입니다. 그는 ObjectWrapper
프록시에 다른 개체를 당신을 허용하지만 프록시 객체와의 모든 상호 작용 절편. 일반적으로 사용되지는 않지만 Python 3를 지원하지는 않지만 스레드 안전을 사용하여 ioloop를 통해 프록시 객체에 대한 모든 액세스를 반송하는 토네이도 기반 비동기 구현 주위에서 스레드 안전 차단 래퍼를 구현하는 데 사용했습니다. concurrent.Future
동기화하고 결과를 반환하는 개체입니다.
기본적으로 프록시 객체에 대한 모든 속성 액세스는 프록시 객체의 결과를 제공합니다. 프록시 객체에 속성을 추가해야하는 경우 __slots__
사용할 수 있습니다.
from peak.util.proxies import ObjectWrapper
class Original(object):
def __init__(self):
self.name = 'The Original'
class ProxyOriginal(ObjectWrapper):
__slots__ = ['proxy_name']
def __init__(self, subject, proxy_name):
# proxy_info attributed added directly to the
# Original instance, not the ProxyOriginal instance
self.proxy_info = 'You are proxied by {}'.format(proxy_name)
# proxy_name added to ProxyOriginal instance, since it is
# defined in __slots__
self.proxy_name = proxy_name
super(ProxyOriginal, self).__init__(subject)
if __name__ == "__main__":
original = Original()
proxy = ProxyOriginal(original, 'Proxy Overlord')
# Both statements print "The Original"
print "original.name: ", original.name
print "proxy.name: ", proxy.name
# Both statements below print
# "You are proxied by Proxy Overlord", since the ProxyOriginal
# __init__ sets it to the original object
print "original.proxy_info: ", original.proxy_info
print "proxy.proxy_info: ", proxy.proxy_info
# prints "Proxy Overlord"
print "proxy.proxy_name: ", proxy.proxy_name
# Raises AttributeError since proxy_name is only set on
# the proxy object
print "original.proxy_name: ", proxy.proxy_name
당신은-본질적으로-사용하지 않습니다 __slots__
.
필요하다고 생각 될 __slots__
때는 실제로 경량 또는 플라이급 디자인 패턴 을 사용하려고합니다 . 더 이상 순수한 Python 객체를 사용하지 않으려는 경우입니다. 대신 배열, 구조체 또는 numpy 배열 주위에 Python 객체와 같은 래퍼를 원합니다.
class Flyweight(object):
def get(self, theData, index):
return theData[index]
def set(self, theData, index, value):
theData[index]= value
클래스와 같은 래퍼는 속성이 없으며 기본 데이터에 작용하는 메소드 만 제공합니다. 메소드를 클래스 메소드로 줄일 수 있습니다. 실제로 기본 데이터 배열에서 작동하는 함수로 축소 될 수 있습니다.
__slots__
있습니까?
__slots__
메모리를 절약하는 최적화 기법입니다. __slots__
Flyweight 디자인 패턴뿐만 아니라 많은 객체가있을 때 이점을 보여줍니다. 둘 다 같은 문제를 해결합니다.
__slots__
Evgeni가 지적한 것처럼 때로는 실제로 답이 될 수 있습니다. 간단한 추론으로 추가 할 수 있습니다 (예 : 먼저 정확성에 집중 한 다음 성능을 추가 할 수 있음).
원래 질문은 메모리뿐만 아니라 일반적인 사용 사례에 관한 것이 었습니다. 따라서 많은 양의 객체를 인스턴스화 할 때 더 큰 성능을 얻는다는 점을 언급해야합니다. 큰 문서를 객체로 구문 분석하거나 데이터베이스에서 구문 분석하는 경우와 같습니다.
다음은 슬롯을 사용하고 슬롯을 사용하지 않고 백만 개의 항목으로 오브젝트 트리를 작성하는 비교입니다. 참조로 나무에 일반 dicts를 사용할 때의 성능 (OSX의 Py2.7.10) :
********** RUN 1 **********
1.96036410332 <class 'css_tree_select.element.Element'>
3.02922606468 <class 'css_tree_select.element.ElementNoSlots'>
2.90828204155 dict
********** RUN 2 **********
1.77050495148 <class 'css_tree_select.element.Element'>
3.10655999184 <class 'css_tree_select.element.ElementNoSlots'>
2.84120798111 dict
********** RUN 3 **********
1.84069895744 <class 'css_tree_select.element.Element'>
3.21540498734 <class 'css_tree_select.element.ElementNoSlots'>
2.59615707397 dict
********** RUN 4 **********
1.75041103363 <class 'css_tree_select.element.Element'>
3.17366290092 <class 'css_tree_select.element.ElementNoSlots'>
2.70941114426 dict
테스트 클래스 (식별, 슬롯의 아파트) :
class Element(object):
__slots__ = ['_typ', 'id', 'parent', 'childs']
def __init__(self, typ, id, parent=None):
self._typ = typ
self.id = id
self.childs = []
if parent:
self.parent = parent
parent.childs.append(self)
class ElementNoSlots(object): (same, w/o slots)
테스트 코드, 상세 모드 :
na, nb, nc = 100, 100, 100
for i in (1, 2, 3, 4):
print '*' * 10, 'RUN', i, '*' * 10
# tree with slot and no slot:
for cls in Element, ElementNoSlots:
t1 = time.time()
root = cls('root', 'root')
for i in xrange(na):
ela = cls(typ='a', id=i, parent=root)
for j in xrange(nb):
elb = cls(typ='b', id=(i, j), parent=ela)
for k in xrange(nc):
elc = cls(typ='c', id=(i, j, k), parent=elb)
to = time.time() - t1
print to, cls
del root
# ref: tree with dicts only:
t1 = time.time()
droot = {'childs': []}
for i in xrange(na):
ela = {'typ': 'a', id: i, 'childs': []}
droot['childs'].append(ela)
for j in xrange(nb):
elb = {'typ': 'b', id: (i, j), 'childs': []}
ela['childs'].append(elb)
for k in xrange(nc):
elc = {'typ': 'c', id: (i, j, k), 'childs': []}
elb['childs'].append(elc)
td = time.time() - t1
print td, 'dict'
del droot
class Child(BaseA, BaseB): __slots__ = ('a', 'b')
empy-slot-parents와 함께 예제를 잡지 못했습니다 . 여기에 fordictproxy
를 올리는 대신 생성 된 이유는 무엇 입니까?AttributeError
c