Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 22: Sobrecarga de operadores


No Python, o comportamento dos operadores é definido por métodos especiais, porém tais métodos só podem ser alterados nas classes abertas. Por convenção, os métodos especiais têm nomes que começam e terminam com "__".

Lista de operadores e os métodos correspondentes:

Operador Método Operação
+ __add__ adição
- __sub__ subtração
* __mul__ multiplicação
/ __div__ divisão
// __floordiv__ divisão inteira
% __mod__ módulo
** __pow__ potência
+ __pos__ positivo
- __neg__ negativo
< __lt__ menor que
> __gt__ maior que
<= __le__ menor ou igual a
>= __ge__ maior ou igual a
== __eq__ igual a
!= __ne__ diferente de
<< __lshift__ deslocamento para esquerda
>> __rshift__ deslocamento para direita
& __and__ e bit-a-bit
| __or__ ou bit-a-bit
^ __xor__ ou exclusivo bit-a-bit
~ __inv__ inversão

Exemplo:

In [1]:
# A classe String deriva de str
class String(str):

    def __sub__(self, s):

            return self.replace(s, '')

s1 = String('The Lamb Lies Down On Broadway')
s2 = 'Down '

print '"%s" - "%s" = "%s"' % (s1, s2, s1 - s2)
"The Lamb Lies Down On Broadway" - "Down " = "The Lamb Lies On Broadway"

Observações:

  • A subtração definida no código não é comutativa (da mesma forma que a adição em strings também não é)
  • A classe str não é aberta, portanto não é possível alterar o comportamento da string padrão do Python. Porém a classe String é aberta.
  • A redefinição de operadores conhecidos pode dificultar a leitura do código.

Coleções

Além de métodos especiais para objetos escalares, existem também métodos especiais para lidar com objetos que funcionam como coleções (da mesma forma que as listas e os dicionários), possibilitando o acesso aos itens que fazem parte da coleção.

Exemplo:

In [3]:
class Mat(object):
    """
    Matriz esparsa
    """

    def __init__(self):
        """
        Inicia a matriz
        """

        self.itens = []
        self.default = 0

    def __getitem__(self, xy):
        """
        Retorna o item para X e Y ou default caso contrário
        """

        i = self.index(xy)
        if i is None:
            return self.default

        return self.itens[i][-1]


    def __setitem__(self, xy, data=0):
        """
        Cria novo item na matriz
        """

        i = self.index(xy)
        if i is not None:
            self.itens.pop(i)
        self.itens.append((xy, data))

    def __delitem__(self, xy):
        """
        Remove um item da matriz
        """

        i = self.index(xy)
        if i is None:
            return self.default
        return self.itens.pop(i)

    def __getslice__(self, x1, x2):
        """
        Seleciona linhas da matriz

        """

        r = []
        for x in xrange(x1, x2 + 1):
            r.append(self.row(x))

        return r

    def index(self, xy):

        for i, item in enumerate(self.itens):
            if xy == item[0]:
                return i
        else:
            return None

    def dim(self):
        """
        Retorna as dimensões atuais da matriz
        """
        x = y = 0
        for xy, data in self.itens:
            if xy[0] > x: x = xy[0]
            if xy[1] > y: y = xy[1]

        return x, y

    def keys(self):
        """
        Retorna as coordenadas preenchidas
        """

        return [xy for xy, data in self.itens]

    def values(self):
        """
        Retorna os valores preenchidos
        """

        return [data for xy, data in self.itens]

    def row(self, x):
        """
        Retorna a linha especificada
        """

        X, Y = self.dim()
        r = []
        for y in xrange(1, Y + 1):
            r.append(self[x,y])

        return r

    def col(self, y):
        """
        Retorna a coluna especificada
        """

        X, Y = self.dim()
        r = []
        for x in xrange(1, X + 1):
            r.append(self[x,y])

        return r

    def sum(self):
        """
        Calcula o somatório
        """
        return sum(self.values())

    def avg(self):
        """
        Calcula a média
        """

        X, Y = self.dim()
        return self.sum() / (X * Y)

    def __repr__(self):
        """
        Retorna uma representação do objeto como texto
        """

        r = 'Dim: %s\n' % repr(self.dim())
        X, Y = self.dim()

        for x in xrange(1, X + 1):
            for y in xrange(1, Y + 1):
                r += ' %s = %3.1f' % (repr((x, y)),
                    float(self.__getitem__((x, y))))
            r += '\n'
        return r


if __name__ == '__main__':

    mat = Mat()
    print '2 itens preenchidos:'
    mat[1, 2] = 3.14
    mat[3, 4] = 4.5
    print mat

    print 'Troca e remoção:'
    del mat[3, 4]
    mat[1, 2] = 5.4
    print mat

    print 'Preenchendo a 3ª coluna:'
    for i in xrange(1, 4):
        mat[i + 1, 3] = i
    print mat

    print '3ª coluna:', mat.col(3)
    print 'Fatia com 2ª a 3ª linha', mat[2:3]
    print 'Somatório:', mat.sum(), 'Média', mat.avg()
2 itens preenchidos:
Dim: (3, 4)
 (1, 1) = 0.0 (1, 2) = 3.1 (1, 3) = 0.0 (1, 4) = 0.0
 (2, 1) = 0.0 (2, 2) = 0.0 (2, 3) = 0.0 (2, 4) = 0.0
 (3, 1) = 0.0 (3, 2) = 0.0 (3, 3) = 0.0 (3, 4) = 4.5

Troca e remoção:
Dim: (1, 2)
 (1, 1) = 0.0 (1, 2) = 5.4

Preenchendo a 3ª coluna:
Dim: (4, 3)
 (1, 1) = 0.0 (1, 2) = 5.4 (1, 3) = 0.0
 (2, 1) = 0.0 (2, 2) = 0.0 (2, 3) = 1.0
 (3, 1) = 0.0 (3, 2) = 0.0 (3, 3) = 2.0
 (4, 1) = 0.0 (4, 2) = 0.0 (4, 3) = 3.0

3ª coluna: [0, 1, 2, 3]
Fatia com 2ª a 3ª linha [[0, 0, 1], [0, 0, 2]]
Somatório: 11.4 Média 0.95
In [1]:
 
Out[1]: