... Segunda parte:
Pra que serve?
Em geral, quando queremos criar uma classe não somos muito exigentes: pedimos que os objetos criados por ela tenha um conjunto de atributos do mesmo tipo, umas operações em comum, e só. Por essa razão, o "comportamento padrão" ao se criar classes é apenas fazer essas definições e pronto. A metaclasse default (type em Python, Class em Ruby[2]), portanto, faz justamente isso.
Entretanto, há casos em que se quer que um conjunto de classes tenha várias coisas em comum. Por exemplo ao se criar um framework com um propósito específico, demandando que toda classe defina as mesmas coisas de novo e de novo (ex.: Django, que implementa o mapeamento objeto-relacional através de uma classe por tabela do BD). Sem metaclasses, seria necessário que o programador definisse toda a funcionalidade comum em cada classe criada. Com elas, pode-se deixar a metaclasse cuidar da criação de fato das classes, e deixar que o programador se concentre somente nos detalhes que interessa a ele.
class Funcionario(Model):
nome = CharField(max_length=30)
empresa = ForeignKey(Empresa)
fulano = Funcionario(nome="Fulano", empresa_id=10)
# A metaclasse definiu o construtor pra nós
isinstance(fulano.nome, CharField) # False
isinstance(fulano.nome, unicode) # True
No exemplo acima (que usa metaclasses indiretamente, ao se herdar de Model), o que se está sendo definido não são os atributos dos objetos criados pela classe Funcionario, e sim regras para mapear atributos simples (ex.: nome é do tipo string, não CharField) a colunas do BD. A estrutura das instâncias de Funcionario em si são inferidas pela metaclasse a partir das informações apresentadas.
fulano.nome.__class__ # unicode (string)
fulano.empresa.__class__ # Empresa (outro Model)
fulano.epresa_id.__class__ # int (o ID da empresa no BD)
fulano.id.__class__ # int (o ID do funcionário no BD)
Como e quando deve ser usada?
Raramente. Praticamente nunca. A menos que você esteja trabalhando em uma framework ou biblioteca genérica - destinada a serem usadas por outros programadores, que irão criar suas próprias classes nela - dificilmente existirá uma situação em que metaclasses são necessárias. Pessoalmente, nunca as utilizei, e tenho dificuldade sequer em imaginar situações em que elas são necessárias. Por isso, sugiro fazer a si mesmo as seguintes perguntas:
Estou criando muitas classes?
Essas classes têm muitas coisas em comum?
E ainda assim não é um caso de herança/mixin?
Essa parte comum é redundante? (i.e. pode ser inferida a partir da parte não-comum)
O computador poderia fazer esse trabalho redundante pra mim?
Se a resposta a essas perguntas for "sim", talvez valha a pena usar metaclasses no seu caso. Sobre como usá-las:
Imagine a "classe ideal" (i.e. o que você, como programador, iria ter que incluir na definição da classe, toda vez; deixando de fora toda parte redundante);
Estabeleça os passos que seriam necessários para transformar essa classe ideal na classe "real" (i.e. aquela que o programa precisa para funcionar - que defina os atributos e operações dos objetos tal como eles devem ter de fato);
Implemente uma metaclasse que realize essa transformação.
Receba uma definição de classe como parâmetro;
Interprete sua estrutura (se a linguagem dá suporte a metaclasses, isso deve ser trivial);
Adicione/remova/altere sua estrutura segundo a lógica estabelecida.
Só para não deixar sem exemplo, vou propor uma alternativa à resposta do Rogers Corrêa: nossa "classe ideal" não deveria ter um construtor atribuindo cada atributo posicional ao self (pois seria redundante - essa informação já está presente no slots). Mas a classe real precisa. Vamos então transformar a classe ideal na classe real mantendo todo o resto intacto:
# Definindo uma metaclasse na forma de uma função
def minha_metaclass(nome, parents, atributos):
# Acha os slots da classe
slots = atributos['__slots__']
# Cria um novo construtor que usa esses slots
def init(self, *args):
for i in range(len(slots)):
setattr(self, slots[i], args[i])
# Acrescenta esse construtor na definição da classe
atributos['__init__'] = init
# Cria a classe de fato (usando type)
return type(nome, parents, atributos)
class Carro(object):
__metaclass__ = minha_metaclass
__slots__ = ['marca', 'modelo', 'ano', 'cor']
c = Carro('Toyota', 'Prius', 2005, 'green')
c.marca # Toyota
c.modelo # Prius
c.ano # 2005
c.cor # green
Nota:
Em Ruby, toda classe é uma instância de Class, enquanto em Python as classes podem ser instâncias de qualquer classe que herde de type. Não tenho experiência com Ruby para comentar sobre o funcionamento de metaclasses nessa linguagem.
Para saber mais metaclasses, indico o excelente livro: Python Fluente - Luciano Ramalho --> https://novatec.com.br/livros/pythonfluente/