출처 :https://www.pythontutorial.net/python-oop/python-__slots__/
Introduction to the Python __slots__
다음은 x 및 y 좌표를 포함하는 두 개의 특성이 있는 Point2D 클래스를 정의합니다.
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point2D({self.x},{self.y})'
Point2D 클래스의 각 인스턴스에는 인스턴스 특성을 저장하는 고유한 __dict__ 특성이 있습니다. 예를 들어:
p = Point2D(10,20)
print(p) # Point2D(10,20)
print(p.__dict__) # {'x': 10, 'y': 20}
기본적으로 Python은 사전을 사용하여 인스턴스 속성을 관리합니다. 사전을 사용하면 런타임에 동적으로 인스턴스에 더 많은 속성을 추가할 수 있습니다. 그러나 메모리 오버헤드도 있습니다. Point2D 클래스에 많은 개체가 있으면 메모리 오버헤드가 많이 발생합니다.
메모리 오버 헤드를 없애기 위해 Python은 슬롯을 도입했습니다. 클래스가 고정된(또는 미리 결정된) 인스턴스 속성만 포함된 경우, 슬롯을 사용하여 Python이 사전 대신 더 간결한 데이터 구조를 사용하도록 지시할 수 있습니다.
예를 들어 Point2D 클래스에 인스턴스 속성이 두 개뿐인 경우 다음과 같이 슬롯에 속성을 지정할 수 있습니다.
class Point2D:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point2D({self.x},{self.y})'
이 예제에서는 클래스에서 사용할 속성 이름을 포함하는 iterable(튜플)을 할당합니다.
이렇게 하면 Python은 클래스의 인스턴스에서 사용하던 __dict__를 사용할 수 없다. 다음과 같은 경우 속성 오류 오류가 발생합니다.
point = Point2D(0, 0)
print(point.__dict__)
Error:
AttributeError: 'Point2D' object has no attribute __dict__
대신 클래스의 인스턴스에 있는 __slots__ 을 표시합니다. 예를 들어:
point = Point2D(0, 0)
print(point.__slots__)
Output:
('x', 'y')
또한 런타임에 동적으로 인스턴스에 더 많은 속성을 추가할 수 없습니다. 다음과 같은 경우 오류가 발생합니다.
point.z = 0
Error:
AttributeError: 'Point2D' object has no attribute 'z'
그러나 클래스에 클래스 속성을 추가할 수 있습니다.
Point2D.color = 'black'
pprint(Point2D.__dict__)
Output:
mappingproxy({'__doc__': None,
'__init__': <function Point2D.__init__ at 0x000001BBBA841310>,
'__module__': '__main__',
'__repr__': <function Point2D.__repr__ at 0x000001BBBA8413A0>,
'__slots__': ('x', 'y'),
'color': 'black',
'x': <member 'x' of 'Point2D' objects>,
'y': <member 'y' of 'Point2D' objects>})
이 코드는 Python이 클래스가 아닌 클래스의 인스턴스에 슬롯을 적용하기 때문에 작동합니다.
Python __slots__ and inheritance
상속의 맥락에서 슬롯을 살펴 보겠습니다.
base class는 슬롯을 사용하지만 sub class는 사용하지 않는 경우
다음은 Point2D 를 base class로 정의하고 Point3D를 Point2D 클래스에서 상속되는 sub class로 정의합니다.
class Point2D:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point2D({self.x},{self.y})'
class Point3D(Point2D):
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
if __name__ == '__main__':
point = Point3D(10, 20, 30)
print(point.__dict__)
Output:
{'z': 30}
Point3D 클래스에는 슬롯이 없으므로 인스턴스는 __dict__ 속성을 갖습니다.
이 경우 하위 클래스 Point3D는 base class(사용 가능한 경우)의 슬롯을 사용하고 인스턴스 dictionary을 사용합니다.
Point3D 클래스에서 슬롯을 사용하도록 하려면 다음과 같이 추가 속성 정의를 할 수 있습니다.
class Point3D(Point2D):
__slots__ = ('z',)
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
base class의 __slots__에 이미 지정된 속성은 지정하지 않습니다.
이제 Point3D 클래스는 x, y 및 z를 포함한 모든 속성에 대해 슬롯을 사용합니다.
base class는 __slots__ 사용하지 않으며 sub class는 사용하는 경우
다음 예제에서는 __slots__ 사용하지 않는 base class를 정의하고 sub class는 사용합니다.
class Shape:
pass
class Point2D(Shape):
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
if __name__ == '__main__':
# use both slots and dict to store instance attributes
point = Point2D(10, 10)
print(point.__slots__)
print(point.__dict__)
# can add the attribute at runtime
point.color = 'black'
print(point.__dict__)
Output:
('x', 'y')
{}
{'color': 'black'}
이 경우 Point2D 클래스의 인스턴스는 __slots__와 dictionary을 모두 사용하여 인스턴스 속성을 저장합니다.
Summary
- Python은 사전을 사용하여 클래스 인스턴스의 인스턴스 속성을 저장합니다. 이렇게 하면 런타임에 인스턴스에 더 많은 속성을 동적으로 추가할 수 있지만 메모리 오버헤드도 발생 할 수 있습니다.
- 클래스에 __slots__ 속성에 미리 결정된 인스턴스 속성이 있는 경우 정의하여 Python이 인스턴스 속성을 저장하기 위해 사전을 사용하지 않도록 지시합니다. __slots__ 는 클래스에 많은 개체가 있는 경우 메모리를 최적화합니다.
'Python Object-oriented Programming' 카테고리의 다른 글
num aliases & @enum.unique Decorator (0) | 2023.04.04 |
---|---|
enumeration (0) | 2023.04.04 |
overriding-method (0) | 2023.04.04 |
inheritance (0) | 2023.04.04 |
delete-property (0) | 2023.04.04 |
댓글