26 de dezembro de 2008

Sobrecarga de Operadores em Python


A série sobre acesso a atributos foi muito boa para que eu fixasse alguns conceitos de OO (Orientação a Objetos) e aprendesse um pouco mais sobre Python. Com isso, foram apresentados os métodos para acesso a atributos __getattr__() e __setattr__() e os métodos para conversão de objetos complexos, criados pelo usuário, para tipos primitivos (que também são objetos em Python, que fique claro) __str__(), __int__(), __float__(), __oct__(), __hex__(), __complex__() e __long__().

Uma outra característica interessante da linguagem é a possibilidade de sobrecarregar operadores, permitindo que o desenvolvedor crie classes cujos objetos possam ser manipulados de forma mais intuitiva. Dessa forma, no caso do exemplo que eu venho citando, da classe Clock, dados dois objetos c1 e c2 é possível que os mesmos sejam somados ou subtraídos como em c3 = c1 + c2 e c3 = c1 - c2. Sem o uso deste método, o desenvolvedor teria que fazer algo como c3 = c1.add(c2), que é menos intuitivo e mais trabalhoso de se fazer.

Acontece que Python permite a sobrecarga de alguns métodos especiais, responsáveis por realizar estas operações. Um exemplo é o método __add__(), que é responsável por sobrecarregar o operador de soma. Assim, caso a classe Clock possua este método implementado, ao se somar dois objetos Clock, o interpretador executa o método __add__() do objeto da esquerda, passando como parâmetro o objeto da direita. Vamos a um exemplo:
class Clock(object):
    def __init__(self, hour_, minute_, second_):
        self.hour = hour_
        self.minute = minute_
        self.second = second_
    
    def __str__(self):  
        return "%0.2d:%0.2d:%0.2d" % (self.hour, self.minute, self.second)
    
    def __add__(self, other):        
        return Clock((self.hour + other.hour) % 24, 
                     (self.minute + other.minute) % 60, 
                     (self.second + other.second) % 60)
    
    def __sub__(self, other):
        hour = self.hour - other.hour
        minute = self.minute - other.minute
        second = self.second - other.second
        
        if hour < 0:
            hour = 24 + hour
        
        if minute < 0:
            minute = 60 + minute
        
        if second < 0:
            second = 60 + second
        
        return Clock(hour, minute, second)
    
    def __eq__(self, other):
        return (self.hour == other.hour and self.minute == other.minute and
                self.second == other.second)
    
    def __ne__(self, other):
        return not(self == other)
    
    def __gt__(self, other):
        if self.hour > other.hour:
            return True
        
        elif self.hour == other.hour:
            if self.minute > other.minute:
                return True
            
            elif self.minute == other.minute:
                if self.second > other.second:
                    return True
                
                else:
                    return False
             
            else:
                return False
        
        else:
            return False
    
    def __ge__(self, other):
        return (self > other) or (self == other)
    
    def __lt__(self, other):
        return not(self > other) and not(self == other)
    
    def __le__(self, other):
        return (self < other) or (self == other) 

# 
# Main 
# 

c1 = Clock(18, 37, 32)
c2 = Clock(20, 0, 30)

print type(c1)

exit(0)

print "c1 object :", c1
print "c2 object :", c2

print "c1  +  c2 =", c1 + c2
print "c1  -  c2 =", c1 - c2
print "c1  == c2 ?", c1 == c2
print "c1  != c2 ?", c1 != c2
print "c1  >  c2 ?", c1 > c2
print "c1  <  c2 ?", c1 < c2
print "c1  >= c2 ?", c1 >= c2
print "c1  <= c2 ?", c1 <= c2
Listagem 1. Classe Clock implementando sobrecarga de operadores.

Neste exemplo, podemos ver implementados os métodos __add__() (10-13), __sub__() (15-29), __eq__() (31-33), __ne__() (35-36), __gt__() (38-57), __ge__() (59-60), __lt__() (62-63) e __le__() (65-66), que sobrecarregam, respectivamente, os operadores +, -, ==, !=, >, >=, < e <=. Como pode ser observado na implementação de cada um, o trabalho consiste basicamente em tratar cada atributo do objeto (self) e daquele que está sendo operado em conjunto com ele (chamado de other, na implementação). É um recurso bastante simples de se implementar e que provê um resultado excelente. Há muitos outros métodos que podem ser sobrecarregados, que possibilitam criar objetos similares aos do exemplo citado, mais adaptados à linguagem e mais fáceis de manipular. Evidentemente, o programador não precisa implementar estes métodos para todo objeto que criar: apenas quando for necessário. ;-)

 

Série Orientação a Objetos: Acessos a Atributos
Leia Também

2 comentários:

  1. E ai Zezim da Porquinha cor-de-rosa! ahuahauhaua

    Kra gostei da postagem. Você pegou alguns dos principais métodos de sobrecargas, exatamente o que estava faltando na postagem anterior.

    O mais interessante é a sobrecarga inteligente, você sabe como o interpretador faz esse reconhecimento?

    Abraço.

    ResponderExcluir
  2. Porquinha cor de rosa... ¬¬'

    Qual sobrecarga inteligente você diz? O fato do interpretador encontrar o sinal de soma, por exemplo, e executar o método __add__()?

    Se for isso, sei que ele faz com todos os objetos. Se uma classe precisa realizar tais operações, ela deve implementar o método e então, o interpretador, ao encontrar um operador, aciona o método específico do operador mais à esquerda (na verdade, aqui entra toda aquela história de precedência de operadores), passando como argumento, o operador da direita.

    Para exemplificar, saiba que isto é o usual no interpretador: >>> '2' + '2' Mas isto também vale: >>> '2'.__add__('2'). Sabe me explicar o por quê?!

    []!

    ResponderExcluir