Métodos especiais
Métodos especiais
class MinhaClasse:
def __init__(self, a, b):
...
p1 = MinhaClasse(2, 5)
p2 = MinhaClasse(-1, 10)
p3 = p1 + p2
p4 = p1 - p2
p5 = p1*p2
p6 = p1**7 + 4*p3
Métodos especiais permitem sintaxes diretas e são reconhecidos pelos duplos _
antes e depois do nome do método:
def __init__(self, ...)
def __call__(self, ...)
def __add__(self, outro)
## sintaxe Python
y = Y(4)
print y(2)
z = Y(6)
print y + z
## O que realmente está acontecendo
Y.__init__(y, 4)
print Y.__call__(y, 2)
Y.__init__(z, 6)
print Y.__add__(y, z)
Vamos aprender mais sobre eles!
Chamando um método especial
Substitua o método valor
pelo método especial __call__
:
class Y:
def __init__(self, v0):
self.v0 = v0
self.g = 9.81
def __call__(self, t):
return self.v0*t - 0.5*self.g*t**2
Agora podemos escrever:
y = Y(3)
v = y(0.1) ## o mesmo que v = y.__call__(0.1) ou Y.__call__(y, 0.1)
Nota:
- A instância
y
se comporta e parece como uma função; O métodovalor(t)
faz a mesma coisa, mas__call__
permite uma sintaxe mais simples e limpa para calcular o valor da função.
Um método especial para a saída
- Em Python, normalmente obtemos uma saída em formato de texto usando o objeto
print
, que funciona para tipos de objetos já embutidos (strings, listas, floats...); - Python não sabe como imprimir objetos de uma classe definida pelo usuário, mas se a classe definir um método
__str__
, o Python vai utilizar esse método para converter um objeto em cadeia de caracteres.
Exemplo:
class Y:
...
def __call__(self, t):
return self.v0*t - 0.5*self.g*t**2
def __str__(self):
return 'v0*t - 0.5*g*t**2; v0=%g' % self.v0
Uso:
>>> y = Y(1.5)
>>> y(0.2)
0.1038
>>> print y
v0*t - 0.5*g*t**2; v0=1.5
Definição dos métodos especiais
Esté a cargo do programador definir a ação dos métodos especiais. Como deveria __add__(self, outro)
ser definido? Isso depende completamente do programador, dependendo do significado de objeto1 + objeto2
.
Um antropoligista estava perguntando a um homem das cavernas primitivo sobre aritmética. Quando o antropologista perguntava, O que dois e dois fazem? o homem das cavernas respondia, cinco. Perguntado para explicar, o homem das cavernas disse, se eu tenho uma corda com dois nós, e outra corda com dois nós, e eu unir as duas cordas para fazer uma, então eu tenho cinco nós.
Métodos especiais para operações aritméticas
c = a + b ## c = a.__add__(b)
c = a - b ## c = a.__sub__(b)
c = a*b ## c = a.__mul__(b)
c = a/b ## c = a.__div__(b)
c = a**e ## c = a.__pow__(e)
Métodos especiais para comparação
a == b ## a.__eq__(b)
a != b ## a.__ne__(b)
a < b ## a.__lt__(b)
a <= b ## a.__le__(b)
a > b ## a.__gt__(b)
a >= b ## a.__ge__(b)
O método especial __repr__
O método especial __repr__
cria p
de eval(repr(p))
class MinhaClasse:
def __init__(self, a, b):
self.a, self.b = a, b
def __str__(self):
"""Retorna uma cadeia de caracteres formatada com a saída"""
return 'a=%s, b=%s' % (self.a, self.b)
def __repr__(self):
"""Retorna uma cadeia de caracteres tal que eval(s) cria self."""
return 'MinhaClasse(%s, %s)' % (self.a, self.b)
Uso
>>> m = MinhaClasse(1, 5)
>>> print m ## chama m.__str__()
a=1, b=5
>>> str(m) ## chama m.__str__()
'a=1, b=5'
>>> s = repr(m) ## chama m.__repr__()
>>> s
'MinhaClasse(1, 5)'
>>> m2 = eval(s) ## o mesmo que m2 = MinhaClasse(1, 5)
>>> m2 ## chama m.__repr__()
'MinhaClasse(1, 5)'
Classe Y reescrita com o método repr
class Y:
"""Classe para a função y(t; v0, g) = v0*t - 0.5*g*t**2."""
def __init__(self, v0):
"""Armazenando parâmetros."""
self.v0 = v0
self.g = 9.81
def __call__(self, t):
"""Avaliando a função."""
return self.v0*t - 0.5*self.g*t**2
def __str__(self):
"""Saída formatada."""
return 'v0*t - 0.5*g*t**2; v0=%g' % self.v0
def __repr__(self):
"""Mostre o código para gerar essa instância."""
return 'Y(%s)' % self.v0
Classe para números complexos
O Python já possui a classe complex
para números complexos, mas implementar tal classe é um bom exemplo pedagógico especialmente com métodos especiais.
Uso esperado:
>>> u = Complexo(2,-1)
>>> v = Complexo(1) ## parte imaginária zero
>>> w = u + v
>>> print w
(3, -1)
>>> w != u
True
>>> u*v
Complexo(2, -1)
>>> u < v
illegal operation "<" for complex numbers
>>> print w + 4
(7, -1)
>>> print 4 - w
(1, 1)
Implementação
class Complexo:
def __init__(self, real, imag=0.0):
self.real = real
self.imag = imag
def __add__(self, outro):
return Complexo(self.real + outro.real,
self.imag + outro.imag)
def __sub__(self, outro):
return Complexo(self.real - outro.real,
self.imag - outro.imag)
def __mul__(self, outro):
return Complexo(self.real*outro.real - self.imag*outro.imag,
self.imag*outro.real + self.real*outro.imag)
def __div__(self, outro):
ar, ai, br, bi = self.real, self.imag, \
outro.real, outro.imag ## forma curta
r = float(br**2 + bi**2)
return Complexo((ar*br+ai*bi)/r, (ai*br-ar*bi)/r)
Métodos adicionais
def __abs__(self):
return sqrt(self.real**2 + self.imag**2)
def __neg__(self): ## defines -c (c is Complexo)
return Complexo(-self.real, -self.imag)
def __eq__(self, outro):
return self.real == outro.real and \
self.imag == outro.imag
def __ne__(self, outro):
return not self.__eq__(outro)
def __str__(self):
return '(%g, %g)' % (self.real, self.imag)
def __repr__(self):
return 'Complexo' + str(self)
def __pow__(self, potencia):
raise NotImplementedError(
'self**potencia ainda não implementado para Complexo')
Refinando os métodos especiais para aritmética
Podemos adicionar um número real a um número complexo?
>>> u = Complexo(1, 2)
>>> w = u + 4.5
...
AttributeError: 'float' object has no attribute 'real'
Problema: vamos assumir que o outro
é Complexo
. Solução:
class Complexo:
...
def __add__(self, outro):
if isinstance(outro, (float,int)):
outro = Complexo(outro)
return Complexo(self.real + outro.real,
self.imag + outro.imag)
## ou
def __add__(self, outro):
if isinstance(outro, (float,int)):
return Complexo(self.real + outro, self.imag)
else:
return Complexo(self.real + outro.real,
self.imag + outro.imag)
Métodos especiais para adição
E se tentarmos:
>>> u = Complexo(1, 2)
>>> w = 4.5 + u
...
TypeError: unsupported operand type(s) for +:
'float' and 'instance'
Problema: o objeto float
do Python não pode ser adicionado a um objeto Complexo
.
Solução: se a classe tem o método especial __radd__(self, other)
, Python o aplica para outro + self
.
class Complexo:
...
def __radd__(self, other):
"""Retorna outro + self."""
## outro + self = self + outro:
return self.__add__(outro)
Métodos especiais para subtração
Operadores para subtração são um pouco mais complicados pois
class Complexo:
...
def __sub__(self, outro):
if isinstance(outro, (float,int)):
outro = Complexo(outro)
return Complexo(self.real - outro.real,
self.imag - outro.imag)
def __rsub__(self, outro):
if isinstance(outro, (float,int)):
outro = Complexo(outro)
return outro.__sub__(self)