Python __slots__

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 Point2Dclasse que possui dois atributos, incluindo xe ycoordenadas:

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 Point2Dclasse 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 Point2Dclasse 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 Point2Dclasse 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 AttributeErrorerro:

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 = 0Linguagem 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 Point2Dcomo a classe base e Point3Dcomo uma subclasse que herda da Point2Dclasse:

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 Point3Dclasse não possui slots, portanto sua instância possui o __dict__atributo. Neste caso, a subclasse Point3Dutiliza slots de sua classe base (se disponível) e utiliza um dicionário de instância.

Se quiser que a Point3Dclasse 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 = zLinguagem de código:  Python  ( python )

Observe que você não especifica os atributos que já estão especificados na __slots__classe base.

Agora, a Point3Dclasse 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 Point2Dclasse 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.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *