23 de dezembro de 2008

Convertendo Objetos Complexos para Tipos Primários em Python


Depois da saga sobre a criação de Getters e Setters em Python, por que não estender as funcionalidades dos nossos objetos? Uma situação que muitos programadores certamente passam é a de ter que transformar um objeto em uma string, inteiro, float etc., seja para imprimir na tela ou para prepará-lo para o banco de dados.

O que se faz normalmente nesta situação é criar um método como *2str, que faz a conversão, retornando uma string, por exemplo. Assim, para a classe Clock, proposta nos textos anteriores, haveria um método como clock2str, que seria chamado como print clock.clock2str(), retornando algo como HH:MM:SS (obviamente, o retorno depende da implementação).

Python oferece uma maneira interessante de realizar esta tarefa. Para isto, basta que o programador implemente, dentro da classe, o método __str__(). Este método não recebe qualquer atributo (exceto pelo self) e deve sempre retornar uma string. Retornar qualquer tipo diferente de string gera um erro em tempo de execução, terminando o programa. A grande vantagem de se utilizar este método é que, para imprimir a representação em string do objeto, basta fazer algo como print clock.

Similar ao __str__(), há o __int__(), que retorna um inteiro que representa o objeto. Neste caso, a conversão pode ser feita com o uso da função embutida, int(). Ao encontrar esta função, o interpretador irá procurar o método __int__() do objeto, executando-o em seguida. Da mesma forma, existem os métodos __float__(), __oct__(), __hex__(), __complex__() e __long__(), que são executados, respectivamente, para as funções float(), oct(), hex(), complex() e long().

Nota: Na versão 3.0, o tipo long foi embutido no int, recebendo o nome deste último. Assim, a função long() foi abolida. Apesar de não ter lido especificamente isso, inferi que o método __long__() também deixou de existir.


Desta forma, podemos converter facilmente os objetos que criamos para um dos tipos primitivos de Python. Esta forma de conversão também possibilita a criação de objetos mais adaptados ao interpretador Python, uma vez que, em vez de fazer a conversão assim: objeto.toint(), pode-se fazê-la assim: int(object). Uma forma padronizada de se converter e que não exige muito esforço.

Como de praxe, segue um código de exemplo, com a classe Clock implementando os métodos citados.
class Clock:
    def __init__(self, hour_, minute_, second_):
        self.hour = hour_
        self.minute = minute_
        self.second = second_
    
    def __getattr__(self, name):
        if name == "hour":
            return self._hour
    
        elif name == "minute":
            return self._minute
    
        elif name == "second":
            return self._second
    
        else:
            raise AttributeError, name
    
    def __setattr__(self, name, value):
        if name == "hour":
            if 0 <= value <= 23:
                self.__dict__["_hour"] = value
    
            else:
                raise ValueError, "Invalid %s value: %s" % (name, value)
    
        elif name == "minute":
            if 0 <= value <= 59:
                self.__dict__["_minute"] = value
    
            else:
                raise ValueError, "Invalid %s value: %s" % (name, value)
    
        elif name == "second":
            if 0 <= value <= 59:
                self.__dict__["_second"] = value
    
            else:
                raise ValueError, "Invalid %s value: %s" % (name, value)
    
        else:
            raise AttributeError, name
    
    
    def __str__(self):
        return "%d:%d:%d" % (self.hour, self.minute, self.second)
    
    def __int__(self):
        return int("%d%d%d" % (self.hour, self.minute, self.second))
    
    def __float__(self):
        return float(int(self))
    
    def __hex__(self):
        return hex(int(self))
    
    def __oct__(self):
        return oct(int(self))
    
    def __complex__(self):
        return complex(int(self))
    
    def __long__(self):
        return long(int(self))

# Main

clock = Clock(22, 48, 28)

print "Object to string.: ", clock
print "Object to int....: ", int(clock)
print "Object to float..: ", float(clock)
print "Object to octal..: ", oct(clock)
print "Object to hex....: ", hex(clock)
print "Object to complex: ", complex(clock)
print "Object to long...: ", long(clock)
Listagem 1. Classe Clock com implementação de conversões para tipos primitivos.


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

2 comentários:

  1. Fala brow!

    Bom texto kra. Você cita uma facilidade imensurável da linguagem.

    Porém, se me permite fazer uma sugestão, você poderia realizar uma nova postagem para essa série como override dos __and__, __or__, __repr__. Você poderia estender essa postagem.

    Abraço brow.

    ResponderExcluir
  2. Fala bro!
    Você me deu uma idéia: vou fazer uma com sobrecarga nos métodos adição e subtração, que eu esqueci os nomes agora.
    Aí será possível isso:

    clock0 = Clock(19, 40, 11)
    clock1 = Clock(23, 0, 9)
    clock2 = clock0 + clock1
    clock3 = clock1 - clock0

    Abraço, brother!

    ResponderExcluir