Resumo : neste tutorial você aprenderá sobre Python __slots__
e como usá-lo para tornar sua classe mais eficiente.
Introdução aos __slots__ do Python
O seguinte define uma Point2D
classe que possui dois atributos, incluindo x
e y
coordenadas:
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point2D({self.x},{self.y})'
Linguagem de código: Python ( python )
Cada instância da Point2D
classe possui seu próprio __dict__
atributo que armazena os atributos da instância . Por exemplo:
point = Point2D(0, 0)
print(point.__dict__)
Linguagem de código: Python ( python )
Por padrão, Python usa dicionários para gerenciar os atributos da instância. O dicionário permite adicionar mais atributos à instância dinamicamente em tempo de execução. No entanto, também apresenta uma certa sobrecarga de memória. Se a Point2D
classe tiver muitos objetos, haverá muita sobrecarga de memória.
Para evitar sobrecarga de memória, Python introduziu os slots. Se uma classe contém apenas atributos de instância fixos (ou predeterminados), você pode usar os slots para instruir o Python a usar uma estrutura de dados mais compacta em vez de dicionários.
Por exemplo, se a Point2D
classe tiver apenas dois atributos de instância, você poderá especificar os atributos nos slots assim:
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})'
Linguagem de código: Python ( python )
Neste exemplo, você atribui um iterável (uma tupla ) que contém os nomes dos atributos que você usará na classe.
Ao fazer isso, o Python não usará o __dict__
para as instâncias da classe. O seguinte causará um AttributeError
erro:
point = Point2D(0, 0)
print(point.__dict__)
Linguagem de código: Python ( python )
Erro:
AttributeError: 'Point2D' object has no attribute __dict__
Linguagem de código: Python ( python )
Em vez disso, você verá na __slots__
instância da classe. Por exemplo:
point = Point2D(0, 0)
print(point.__slots__)
Linguagem de código: Python ( python )
Saída:
('x', 'y')
Linguagem de código: Python ( python )
Além disso, você não pode adicionar mais atributos à instância dinamicamente em tempo de execução. O seguinte resultará em um erro:
point.z = 0
Linguagem de código: Python ( python )
Erro:
AttributeError: 'Point2D' object has no attribute 'z'
Linguagem de código: Python ( python )
No entanto, você pode adicionar os atributos de classe à classe:
Point2D.color = 'black'
pprint(Point2D.__dict__)
Linguagem de código: Python ( python )
Saída:
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>})
Linguagem de código: Python ( python )
Este código funciona porque o Python aplica os slots às instâncias da classe, não à classe.
Python __slots__ e herança única
Vamos examinar os slots no contexto da herança.
A classe base usa os slots, mas a subclasse não
O seguinte define Point2D
como a classe base e Point3D
como uma subclasse que herda da Point2D
classe:
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__)
Linguagem de código: Python ( python )
Saída:
{'z': 30}
Linguagem de código: Python ( python )
A Point3D
classe não possui slots, portanto sua instância possui o __dict__
atributo. Neste caso, a subclasse Point3D
utiliza slots de sua classe base (se disponível) e utiliza um dicionário de instância.
Se quiser que a Point3D
classe use slots, você pode definir atributos adicionais como este:
class Point3D(Point2D):
__slots__ = ('z',)
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
Linguagem de código: Python ( python )
Observe que você não especifica os atributos que já estão especificados na __slots__
classe base.
Agora, a Point3D
classe usará slots para todos os atributos, incluindo x, y e z.
A classe base não usa __slots__ e a subclasse não
O exemplo a seguir define uma classe base que não usa o __slots__
e a subclasse usa:
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__)
Linguagem de código: Python ( python )
Saída:
('x', 'y')
{'color': 'black'}
Linguagem de código: Python ( python )
Nesse caso, as instâncias da Point2D
classe usam __slots__
um dicionário para armazenar os atributos da instância.
Resumo
- Python usa dicionários para armazenar atributos de instâncias de uma classe. Isso permite adicionar dinamicamente mais atributos às instâncias em tempo de execução, mas também cria uma sobrecarga de memória.
- Defina
__slots__
na classe se ela possui atributos de instâncias pré-determinados para instruir o Python a não usar dicionários para armazenar atributos de instância. Otimiza a__slots__
memória se a classe tiver muitos objetos.