Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 18: Classes


Objetos são abstrações computacionais que representam entidades, com suas qualidades (atributos) e ações (métodos) que estas podem realizar. A classe é a estrutura básica do paradigma de orientação a objetos, que representa o tipo do objeto, um modelo a partir do qual os objetos serão criados.

Exemplo de classes

Por exemplo, a classe Canino descreve as características e ações dos caninos em geral, enquanto o objeto Bandit representa um canino em particular.

Os atributos são estruturas de dados que armazenam informações sobre o objeto e os métodos são funções associadas ao objeto, que descrevem como o objeto se comporta.

No Python, novos objetos são criados a partir das classes através de atribuição. O objeto é uma instância da classe, que possui características próprias. Quando um novo objeto é criado, o construtor da classe é executado. Em Python, o construtor é um método especial, chamado __new__(). Após a chamada ao construtor, o método __init__() é chamado para inicializar a nova instância.

Um objeto continua existindo na memória enquanto existir pelo menos uma referência a ele. O interpretador Python possui um recurso chamado coletor de lixo (Garbage Collector) que limpa da memória objetos sem referências. Quando o objeto é apagado, o método especial __done__() é evocado. Funções ligadas ao coletor de lixo podem ser encontradas no módulo gc.

Classe e Objeto

Em Python:

  • Tudo é objeto, mesmo os tipos básicos, como números inteiros.
  • Tipos e classes são unificados.
  • Os operadores são na verdade chamadas para métodos especiais.
  • As classes são abertas (menos para os tipos builtins).

Métodos especiais são identificados por nomes no padrão __metodo__() (dois sublinhados no início e no final do nome) e definem como os objetos derivados da classe se comportarão em situações particulares, como em sobrecarga de operadores.

No Python, existem dois tipos de classes, chamadas old style e new style. As classes new style são derivadas da classe object e podem utilizar recursos novos das classes do Python, como properties e metaclasses. As properties são atributos calculados em tempo de execução através de métodos, enquanto as metaclasses são classes que geram classes, com isso permitem personalizar o comportamento das classes. As classes old style são uma herança das versões antigas do Python, mantidas para garantir compatibilidade com código legado.

Sintaxe:

In [ ]:
class Classe(supcl1, supcl2):
    """
    Isto é uma classe
    """
    clsvar = []

    def __init__(self, args):
        """
        Inicializador da classe
        """
        <bloco de código>

    def __done__(self):
        """
        Destrutor da classe
        """
        <bloco de código>

    def metodo(self, params):
        """
        Método de objeto
        """
        <bloco de código>

    @classmethod
    def cls_metodo(cls, params):
        """
        Método de classe
        """
        <bloco de código>

    @staticmethod
    def est_metodo(params):
        """
        Método estático
        """
        <bloco de código>


obj = Classe()
obj.metodo()

Classe.cls_metodo()
Classe.est_metodo()

Métodos de objeto podem usar atributos e outros métodos do objeto. A variável self, que representa o objeto e também precisa ser passado de forma explícita. O nome self é uma convenção, assim como cls, podendo ser trocado por outro nome qualquer, porém é considerada como boa prática manter o nome.

Métodos de classe podem usar apenas atributos e outros métodos de classe. O argumento cls representa a classe em si, precisa ser passado explicitamente como primeiro parâmetro do método.

Métodos estáticos são aqueles que não tem ligação com atributos do objeto ou da classe. Funcionam como as funções comuns.

Exemplo de classe:

In [2]:
class Cell(object):
    """
    Classe para células de planilha
    """

    def __init__(self, formula='""', format='%s'):
        """
        Inicializa a célula
        """

        self.formula = formula
        self.format = format

    def __repr__(self):
        """
        Retorna a representação em string da célula
        """

        return self.format % eval(self.formula)


print Cell('123**2')
print Cell('23*2+2')
print Cell('abs(-1.45 / 0.3)', '%2.3f')
15129
48
4.833

O método __repr__() é usado internamente pelo comando print para obter uma representação do objeto em forma de texto.

Em Python, não existem variáveis e métodos privados (que só podem ser acessados a partir do próprio objeto). Ao invés disso, é usada uma convenção, usar um nome que comece com sublinhado (_), deve ser considerado parte da implementação interna do objeto e sujeito a mudanças sem aviso prévio. Além disso, a linguagem oferece uma funcionalidade chamada Name Mangling, que acrescenta na frente de nomes que iniciam com dois sublinhados (__), um sublinhado e o nome da classe.

Exemplo:

In [3]:
class Calc:

    def __init__(self, formula, **vars):

        self.formula = formula
        self.vars = vars

        self.__recalc()
    
    def __recalc(self):

        self.__res = eval(self.formula, self.vars)

    def __repr__(self):

        self.__recalc()
        return str(self.__res)


formula = '2*x + 3*y + z**2'
calc = Calc(formula, x=2, y=3, z=1)

print 'fórmula:', calc.formula
print 'x =', calc.vars['x'],'-> calc =', calc
calc.vars['x'] = 4
print 'x =', calc.vars['x'],'-> calc =', calc
print dir(calc)
fórmula: 2*x + 3*y + z**2
x = 2 -> calc = 14
x = 4 -> calc = 18
['_Calc__recalc', '_Calc__res', '__doc__', '__init__', '__module__', '__repr__', 'formula', 'vars']

O método __recalc() aparece como _Calc__recalc() e o atributo __res como _Calc__res para fora do objeto.

Classes abertas

No Python, as classes que não são builtins podem ser alteradas em tempo de execução, devido a natureza dinâmica da linguagem. É possível acrescentar métodos e atributos novos, por exemplo. A mesma lógica se aplica aos objetos.

Exemplo de como acrescentar um novo método:

In [4]:
class User(object):
    """Uma classe bem simples.
    """
    def __init__(self, name):
        """Inicializa a classe, atribuindo um nome
        """
        self.name = name


# Um novo método para a classe
def set_password(self, password):
    """Troca a senha
    """
    self.password = password

print 'Classe original:', dir(User)

# O novo método é inserido na classe
User.set_password = set_password
print 'Classe modificada:', dir(User)

user = User('guest')
user.set_password('guest')

print 'Objeto:', dir(user)
print 'Senha:', user.password
Classe original: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
Classe modificada: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'set_password']
Objeto: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'password', 'set_password']
Senha: guest

A classe modificada passou a ter um novo método: set_password().

In [1]:
 
Out[1]: