Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 23: Metaclasses


Em uma linguagem orientada a objeto aonde (quase) tudo são objetos e todo o objeto tem uma classe, é natural que as classes também sejam tratadas como objetos.

Metaclasse é uma classe cujas as instâncias são classes, sendo assim, a metaclasse define o comportamento das classes derivadas a partir dela. Em Python, a classe type é uma metaclasse e pode ser usada para criar novas metaclasses.

Exemplo de metaclasse criada a partir de type:

In [3]:
class Singleton(type):
    """
    Metaclasse Singleton
    """

    def __init__(cls, name, bases, dic):

        type.__init__(cls, name, bases, dic)

        # Retorna o próprio objeto na cópia
        def __copy__(self):
            return self

        # Retorna o próprio objeto na cópia recursiva
        def __deepcopy__(self, memo=None):
            return self

        cls.__copy__ = __copy__
        cls.__deepcopy__ = __deepcopy__

    def __call__(cls, *args, **kwargs):

        # Chamada que cria novos objetos,
        # aqui retorna sempre o mesmo
        try:
            return cls.__instance

        # Se __instance não existir, então crie...
        except AttributeError:

            # A função super() pesquisa na MRO
            # a partir de Singleton
            cls.__instance = super(Singleton,
                cls).__call__(*args, **kwargs)
            return cls.__instance


import MySQLdb


class Con(object):
    """
    Classe de conexão única
    """

    # Define a metaclasse desta classe
    __metaclass__ = Singleton

    def __init__(self):

        # Cria uma conexão e um cursor
        con = MySQLdb.connect(user='root', passwd='root123')
        self.db = con.cursor()
        # Sempre será usado o mesmo
        # objeto de cursor


class Log(object):
    """
    Classe de log
    """

    # Define a metaclasse desta classe
    __metaclass__ = Singleton

    def __init__(self):

        # Abre o arquivo de log para escrita
        self.log = file('msg.log', 'w')
        # Sempre será usado o mesmo
        # objeto de arquivo

    def write(self, msg):

        print msg
        # Acrescenta as mensagens no arquivo
        self.log.write(str(msg) + '\n')


# Conexão 1
con1 = Con()
Log().write('con1 id = %d' % id(con1))
con1.db.execute('show processlist')
Log().write(con1.db.fetchall())

# Conexão 2
con2 = Con()
Log().write('con2 id = %d' % id(con2))
con2.db.execute('show processlist')
Log().write(con2.db.fetchall())

import copy

# Conexão 3
con3 = copy.copy(con1)
Log().write('con3 id = %d' % id(con3))
con3.db.execute('show processlist')
Log().write(con2.db.fetchall())
con1 id = 63759504
((87L, 'root', 'localhost', None, 'Sleep', 68L, '', None), (88L, 'root', 'localhost', None, 'Query', 1L, None, 'show processlist'))
con2 id = 63759504
((87L, 'root', 'localhost', None, 'Sleep', 68L, '', None), (88L, 'root', 'localhost', None, 'Query', 0L, None, 'show processlist'))
con3 id = 63759504
((87L, 'root', 'localhost', None, 'Sleep', 68L, '', None), (88L, 'root', 'localhost', None, 'Query', 0L, None, 'show processlist'))

A partir da versão 2.6, o Python passou a suportar Abstract Base Classes, que são metaclasses que permitem forçar a implementação de determinados métodos e atributos das classes e subclasses derivadas.

O módulo abc define a metaclasse ABCMeta e os decoradores abstractmethod e abstractproperty que identificam os métodos e propriedades que devem ser implementadas.

In [4]:
from abc import ABCMeta, abstractmethod


class Nave(object):

    __metaclass__ = ABCMeta

    @abstractmethod
    def mover(self, x0, x1, v):

        # Sem implementação
        pass


class Zeppelin(Nave):

    def mover(self, x0, x1, v):
        """
        A partir da posição inicial e final e da velocidade
        calcula o tempo da viagem
        """
        d = x1 - x0
        t = v * d
        return t


class Hovercraft(Nave):

    # Esta classe não implementa o método mover()
    pass

z = Zeppelin()

# Objeto que não implementa o método abstrato
# Isso causa uma exceção TypeError
h = Hovercraft()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-e9d96f909d21> in <module>()
     34 # Objeto que não implementa o método abstrato
     35 # Isso causa uma exceção TypeError
---> 36 h = Hovercraft()

TypeError: Can't instantiate abstract class Hovercraft with abstract methods mover

A avaliação da existência dos métodos abstratos ocorre durante o processo de criação de objetos a partir da classe, porém esta não leva em conta os parâmetros dos métodos.

In [1]:
 
Out[1]: