Pergunta

O que são metaclasses e o que vamos usá-los para?

Foi útil?

Solução

A metaclasse é a classe de uma classe. Uma classe define como um exemplo da classe (isto é, um objecto) enquanto se comporta uma metaclasse define como um comporta classe. Uma classe é uma instância de uma metaclasse.

Enquanto em Python você pode usar chamáveis ??arbitrárias para metaclasses (como Jerub espectáculos), a melhor abordagem é para torná-lo uma classe real em si. type é o metaclass usual em Python. type é por si só uma classe, e é seu próprio tipo. Você não será capaz de recriar algo como type puramente em Python, mas Python engana um pouco. Para criar seu próprio metaclass em Python você realmente só quero type subclasse.

A metaclasse é mais comumente usado como uma classe de fábrica. Quando você cria um objeto chamando a classe, o Python cria uma nova classe (quando se executa a instrução 'classe') chamando a metaclasse. Combinado com os métodos __init__ e __new__ normais, metaclasses, portanto, permitir que você faça 'coisas extra' ao criar uma classe, como registrar a nova classe com algum registro ou substituir a classe com algo completamente diferente.

Quando a instrução class é executado, Python primeiro executa o corpo do comunicado class como um bloco normal do código. O espaço de nomes resultante (um dicionário) mantém os atributos da classe-de-ser. O metaclass é determinado ao olhar para as baseclasses da classe-a-ser (metaclasses são herdadas), no atributo __metaclass__ da classe-de-ser (se qualquer) ou a variável global __metaclass__. A metaclasse é então chamado com o nome, bases e atributos da classe para instanciar-lo.

No entanto, metaclasses realmente definir o tipo de uma classe, não apenas uma fábrica para ele, assim que você pode fazer muito mais com eles. Você pode, por exemplo, definir métodos normais no metaclass. Estes metaclasse-métodos são como classmethods em que eles podem ser chamados na classe sem uma instância, mas eles também não são como classmethods em que não pode ser chamado em uma instância da classe. type.__subclasses__() é um exemplo de um método no metaclass type. Você também pode definir os métodos normais 'mágicos', como __add__, __iter__ e __getattr__, para implementar ou alterar como a comporta de classe.

Aqui está um exemplo agregada dos pedaços:

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__

Outras dicas

Classes como objetos

Antes de metaclasses compreensão, você precisa master classes em Python. E Python tem uma ideia muito peculiar do que as classes são, emprestado da linguagem Smalltalk.

Na maioria das linguagens, as aulas são apenas pedaços de código que descrevem como produzir um objeto. Isso é meio verdade em Python demasiado:

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Mas as aulas são mais do que isso em Python. As aulas são objetos também.

Sim, objetos.

Assim que você usa a palavra-chave class, Python o executa e cria um objeto. A instrução

>>> class ObjectCreator(object):
...       pass
...

cria na memória um objeto com o nome "ObjectCreator".

Este objeto (classe) é-se capaz de criar objetos (as instâncias), e é por isso que é uma classe .

Mas ainda assim, é um objeto, e, portanto:

  • você pode atribuí-lo a uma variável
  • você pode copiá-lo
  • Você pode adicionar atributos a ele
  • você pode passá-lo como um parâmetro de função

por exemplo:.

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

aulas Criando dinamicamente

Como as classes são objetos, você pode criá-los em tempo real, como qualquer objeto.

Primeiro, você pode criar uma classe em uma função usando class:

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

Mas não é tão dinâmico, desde que você ainda tem que escrever toda a classe si mesmo.

Como as classes são objetos, eles devem ser geradas por alguma coisa.

Quando você usa a palavra-chave class, Python cria este objeto automaticamente. Mas como a maioria das coisas em Python, dá-lhe uma maneira de fazê-lo manualmente.

Lembre-se a função type? A boa função de idade que lhe permite saber o que digite um objeto é:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

Bem, type tem uma habilidade completamente diferente, também pode criar classes na mosca. type pode levar a descrição de uma classe como parâmetros, e retornar uma classe.

(eu sei, que é bobagem que a mesma função pode ter dois usos completamente diferentes de acordo com os parâmetros que você passa para ele. É um problema devido a para trás compatibilidade em Python)

type funciona desta maneira:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

por exemplo:.

>>> class MyShinyClass(object):
...       pass

pode ser criado manualmente desta forma:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

Você notará que nós usamos "MyShinyClass" como o nome da classe e como a variável para manter a referência de classe. Eles podem ser diferentes, mas não há razão para as coisas complicar.

type aceita um dicionário para definir os atributos da classe. Assim:

>>> class Foo(object):
...       bar = True

pode ser traduzido para:

>>> Foo = type('Foo', (), {'bar':True})

E usado como uma classe normal:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

E, claro, você pode herdar a partir dele, assim:

>>>   class FooChild(Foo):
...         pass

seria:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

Eventualmente, você vai querer adicionar métodos para a sua classe. Apenas definir uma função com a assinatura apropriada e atribuí-lo como um atributo.

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

E você pode adicionar ainda mais métodos depois de criar dinamicamente a classe, assim como a adição de métodos para um objeto de classe normalmente criado.

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

Você vê para onde vamos:. Em Python, as classes são objetos, e você pode criar uma classe em tempo real, de forma dinâmica

Isto é o que Python faz quando você usa a palavra-chave class, e fá-lo usando uma metaclasse.

O que são metaclasses (finalmente)

Metaclasses são as 'coisas' que cria classes.

Você definir classes, a fim de criar objetos, certo?

Mas nós aprendemos que classes Python são objetos.

Bem, metaclasses são o que criam esses objetos. Eles são classes das classes, você pode imaginá-los desta maneira:

MyClass = MetaClass()
my_object = MyClass()

Você viu que type permite que você faça algo como isto:

MyClass = type('MyClass', (), {})

É porque a função type é na verdade uma metaclasse. type é o metaclass Python usa para criar todas as classes nos bastidores.

Agora você quer saber por que diabos está escrito em letras minúsculas, e não Type?

Bem, eu acho que é uma questão de coerência com str, a classe que cria cordas objetos e int a classe que cria inteiro objectos. type é apenas a classe que cria objetos de classe.

Você vê que, verificando o atributo __class__.

Tudo, e eu quero dizer tudo, é um objeto em Python. Isso inclui ints, strings, funções e classes. Todos eles são objetos. E todos eles têm foi criado a partir de uma classe:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

Agora, qual é a __class__ de qualquer __class__?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

Assim, uma metaclasse é apenas o material que cria objetos de classe.

Você pode chamá-lo de 'fábrica de classe' se desejar.

type é o built-in metaclasse usos Python, mas é claro, você pode criar o seu própria metaclasse.

O __metaclass__ atributo

Em Python 2, você pode adicionar um atributo __metaclass__ quando você escrever uma classe (ver secção seguinte para a sintaxe Python 3):

class Foo(object):
    __metaclass__ = something...
    [...]

Se você fizer isso, Python usará o metaclass para criar o Foo classe.

Cuidado, é complicado.

Você escreve class Foo(object) primeira, mas a Foo objeto de classe não é criada na memória ainda.

Python irá procurar __metaclass__ na definição de classe. Se se considerar, ele vai usá-lo para criar o Foo classe de objeto. Se isso não acontecer, ele irá usar type para criar a classe.

Leia isso várias vezes.

Quando você faz:

class Foo(Bar):
    pass

Python faz o seguinte:

Existe um atributo __metaclass__ em Foo?

Se sim, criar na memória de um objeto de classe (eu disse um objeto de classe, fique comigo aqui), com o nome Foo usando o que está em __metaclass__.

Se Python não consegue encontrar __metaclass__, ele vai olhar para uma __metaclass__ no nível de módulo, e tentar fazer o mesmo (mas apenas para as classes que fazer qualquer coisa que não herdam, basicamente aulas de estilo antigo).

Então, se ele não pode encontrar qualquer __metaclass__ em tudo, ele usará o Bar do (primeiro pai) própria metaclasse (que pode ser o type padrão) para criar o objeto de classe.

Tenha cuidado aqui que o atributo __metaclass__ não será herdada, o metaclass do pai (Bar.__class__) será. Se Bar usado um atributo __metaclass__ que Bar criado com type() (e não type.__new__()), as subclasses não herdarão esse comportamento.

Agora, a grande questão é, o que você pode colocar em __metaclass__?

A resposta é:. Algo que pode criar uma classe

E o que pode criar uma classe? type, ou qualquer coisa que subclasses ou usa-lo.

Metaclasses em Python 3

A sintaxe para definir o metaclass foi alterado no Python 3:

class Foo(object, metaclass=something):
    ...

i. o atributo __metaclass__ não é mais usado, em favor de um argumento de palavra-chave na lista de classes base.

O comportamento de metaclasses no entanto permanece em grande parte o mesmo .

Uma coisa adicionado a metaclasses em Python 3 é que você também pode passar atributos como chave-argumentos em uma metaclasse, assim:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
    ...

Leia a seção abaixo como alças python isso.

Custom metaclasses

O objetivo principalde uma metaclasse é mudar a classe automaticamente, quando ele é criado.

Você costuma fazer isso para APIs, onde pretende criar classes correspondentes a contexto atual.

Imagine um exemplo estúpido, onde você decide que todas as classes em seu módulo devem ter seus atributos escrito em letras maiúsculas. Existem várias maneiras de fazer isso, mas é uma maneira de __metaclass__ conjunto no nível de módulo.

Desta forma, todas as classes deste módulo será criado usando este metaclass, e só temos de dizer a metaclasse para transformar todos os atributos para maiúsculas.

Felizmente, __metaclass__ pode realmente ser qualquer exigível, não precisa ser um classe formais (eu sei, algo com 'classe' em seu nome não precisa ser uma classe, vai entender ... mas é útil).

Então, vamos começar com um exemplo simples, usando uma função.

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """

    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

Agora, vamos fazer exatamente o mesmo, mas usando uma classe real para um metaclass:

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

Mas isso não é realmente OOP. Chamamos type directamente e nós não substituem ou ligue para o __new__ pai. Vamos fazê-lo:

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

Você deve ter notado a upperattr_metaclass argumento extra. Há sim nada de especial nisso: __new__ sempre recebe a classe que está definido no, como o primeiro parâmetro. Assim como você tem self por métodos comuns que recebem a instância como primeiro parâmetro, ou a classe de definição para métodos de classe.

É claro, os nomes que eu usei aqui são longos por causa da clareza, mas como para self, todos os argumentos têm nomes convencionais. Assim, uma verdadeira produção metaclass ficaria assim:

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

Podemos torná-lo ainda mais limpo usando super, o que vai facilitar herança (porque sim, você pode ter metaclasses, herdando de metaclasses, herdando de tipo):

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

Oh, e em python 3 se você fizer esta chamada com argumentos de palavra-chave, como esta:

class Foo(object, metaclass=Thing, kwarg1=value1):
    ...

Ele traduz a isso no metaclass para usá-lo:

class Thing(type):
    def __new__(cls, clsname, bases, dct, kwargs1=default):
        ...

É isso. Não há realmente nada mais sobre metaclasses.

A razão por trás da complexidade do código usando metaclasses não é porque de metaclasses, é porque você costuma usar metaclasses para fazer coisas torcida contando com introspecção, manipulação de herança, tais como vars __dict__, etc.

Na verdade, metaclasses são especialmente úteis para fazer magia negra, e, portanto, coisas complicadas. Mas por si mesmos, eles são simples:

  • interceptar uma criação de classe
  • modificar a classe
  • retornar a classe modificado

Por que você usar classes metaclasses em vez de funções?

Desde __metaclass__ pode aceitar qualquer exigível, por que você usar uma classe uma vez que é obviamente mais complicado?

Existem várias razões para isso:

  • A intenção é clara. Quando você lê UpperAttrMetaclass(type), você sabe o que vai seguir
  • Você pode usar OOP. Metaclass pode herdar de metaclass, métodos override pais. Metaclasses ainda pode usar metaclasses.
  • As subclasses de uma classe serão instâncias de sua metaclass se você especificou um metaclass de classe, mas não com uma função metaclass.
  • Você pode estruturar o seu código melhor. Você nunca utilizar metaclasses para algo tão trivial como o exemplo acima. Geralmente é algo complicado. Ter a capacidade de fazer vários métodos e agrupá-los em uma classe é muito útil para tornar o código mais fácil de ler.
  • Você pode ligar no __new__, __init__ e __call__. O que permitirá -lo a fazer coisas diferentes. Apesar de, normalmente você pode fazer tudo isso em __new__, algumas pessoas são apenas mais confortável usando __init__.
  • Estes são chamados metaclasses, droga! Deve significar alguma coisa!

Por que você usaria metaclasses?

Agora, a grande questão. Por que você iria usar algum recurso propenso erro obscura?

Bem, normalmente você não:

Metaclasses são mais profundas mágica que 99% dos usuários nunca devem se preocupar. Se você quer saber se você precisar deles, Você não (as pessoas que realmente precisa-los saber com certeza que eles precisam, e não precisa de um explicação sobre o porquê).

Python Guru Tim Peters

O principal caso de uso para uma metaclasse é a criação de uma API. Um exemplo típico disso é o Django ORM.

Ele permite que você definir algo como isto:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

Mas se você fizer isso:

guy = Person(name='bob', age='35')
print(guy.age)

Não vai retornar um objeto IntegerField. Ele irá retornar um int, e pode até mesmo levá-lo directamente a partir do banco de dados.

Isto é possível porque define models.Model __metaclass__ e ele usa alguma mágica que irá transformar o Person que acabou de definir com simples declarações em um gancho complexo para um campo de banco de dados.

Django torna algo complexo simples olhar por expor uma API simples e usando metaclasses, recriando código deste API para fazer o trabalho real nos bastidores.

A última palavra

Em primeiro lugar, você sabe que as classes são objetos que podem criar instâncias.

Bem, na verdade, as aulas são eles próprios casos. De metaclasses.

>>> class Foo(object): pass
>>> id(Foo)
142630324

Tudo é um objeto em Python, e eles são todos ou instâncias de classes ou instâncias de metaclasses.

Com exceção de type.

type é na verdade sua própria metaclasse. Isso não é algo que você poderia reproduzir em puro Python, e é feito por engano um pouco na implementação nível.

Em segundo lugar, metaclasses são complicados. Você não pode querer usá-los para alterações classe muito simples. Você pode mudar de classe, utilizando duas técnicas diferentes:

99% do tempo você precisa classe alteração, você é melhor fora de usar estes.

Mas 98% do tempo, você não precisa alteração de classe de todo.

Nota, esta resposta é para Python 2.x como foi escrito em 2008, metaclasses são ligeiramente diferentes em 3.x.

Metaclasses são o molho secreto que fazem 'classe' de trabalho. O metaclass padrão para um novo objeto de estilo é chamado de 'tipo'.

class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

Metaclasses tomar 3 args. ' nome ', ' bases ' e ' dict '

Aqui é onde começa secretos. Olhar para onde o nome, bases e o dict vem nesta definição exemplo de classe.

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

Permite definir uma metaclasse que irá demonstrar como. ' classe: ' chama-lhe

def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return "yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output => 
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

E agora, um exemplo que realmente significa algo, isso vai fazer automaticamente as variáveis ??na lista de "atributos" set na classe, e definido como Nenhum.

def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

Note que o comportamento mágico que os ganhos 'Initalised' por ter a init_attributes metaclass não é passado para uma subclasse de Initalised.

Aqui está um exemplo ainda mais concreto, mostrando como você pode subclasse 'tipo' para fazer uma metaclasse que executa uma ação quando a classe é criada. Este é bastante complicado:

class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

 class Foo(object):
     __metaclass__ = MetaSingleton

 a = Foo()
 b = Foo()
 assert a is b

Um uso para metaclasses é a adição de novas propriedades e métodos para uma instância automaticamente.

Por exemplo, se você olhar para Django modelos , sua definição parece um pouco confuso. É como se você está apenas definindo propriedades de classe:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

No entanto, em tempo de execução dos objetos Person são preenchidos com todos os tipos de métodos úteis. Veja a fonte para alguns incrível metaclassery.

Outros têm explicou como metaclasses trabalho e como eles se encaixam no sistema de tipo Python. Aqui está um exemplo do que pode ser usado para. Em uma estrutura de teste que eu escrevi, eu queria manter o controle da ordem em que as classes foram definidos, para que eu pudesse depois instanciar-los nesta ordem. Eu achei mais fácil de fazer isso usando uma metaclasse.

class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

Tudo o que é uma subclasse de MyType em seguida, recebe um atributo _order classe que registra a ordem em que as classes foram definidos.

Eu acho que a introdução ONLamp à programação metaclasse é bem escrito e dá uma muito boa introdução ao tema, apesar de estar vários anos já.

http://www.onlamp.com/ pub / a / python / 2003/04/17 / metaclasses.html (arquivado em https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a /python/2003/04/17/metaclasses.html )

Em resumo: classe A é um modelo para a criação de uma instância, uma metaclasse é um modelo para a criação de uma classe. Ele pode ser facilmente visto que em Python classes precisam ser de primeira classe objetos demais para permitir esse comportamento.

Eu nunca escrevi um eu mesmo, mas eu acho que um dos usos mais agradáveis ??de metaclasses pode ser visto no quadro Django . As classes do modelo utiliza uma abordagem metaclass para permitir um estilo declarativo de escrever novos modelos ou classes de formulário. Enquanto o metaclass está criando a classe, todos os membros têm a possibilidade de personalizar a própria classe.

A única coisa que resta a dizer é:. Se você não sabe quais são metaclasses, a probabilidade de que você não vai precisar deles é de 99%

Quais são metaclasses? O que você usá-los para?

TLDR: A metaclass instancia e define o comportamento de uma classe como um comportamento instancia de classe e define para uma instância.

Pseudocódigo:

>>> Class(...)
instance

O acima deve parecer familiar. Bem, onde é que Class vem? É uma instância de uma metaclasse (também pseudocódigo):

>>> Metaclass(...)
Class

No código real, podemos passar o metaclass padrão, type, tudo o que precisamos para instanciar uma classe e nós temos uma classe:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

Colocá-lo de forma diferente

  • A classe é uma instância como uma metaclasse é uma classe.

    Quando instanciar um objeto, temos um exemplo:

    >>> object()                          # instantiation of class
    <object object at 0x7f9069b4e0b0>     # instance
    

    Da mesma forma, quando definimos uma classe explicitamente com a metaclass padrão, type, instanciamos-lo:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
    
  • Dito de outra forma, uma classe é uma instância de uma metaclasse:

    >>> isinstance(object, type)
    True
    
  • Coloque uma terceira via, uma metaclasse é de classe de uma classe.

    >>> type(object) == type
    True
    >>> object.__class__
    <class 'type'>
    

Quando você escreve uma definição de classe e Python executa-lo, ele usa um metaclass para instanciar o objeto de classe (que, por sua vez, ser usada para casos instanciar dessa classe).

Assim como podemos usar definições de classe para alterar como instâncias de objeto personalizado comportar, podemos usar uma definição de classe metaclass para mudar a maneira um objeto de classe se comporta.

O que eles podem ser usados ??para? Do docs :

Os usos potenciais para metaclasses são ilimitadas. Algumas idéias que foram explorados incluem o registo, verificação de interface, a delegação automática, criação propriedade automática, proxies, frameworks, e bloqueio de recursos / sincronização automática.

No entanto, normalmente é incentivado para os usuários a evitar o uso de metaclasses menos que seja absolutamente necessário.

Você usa um metaclass cada vez que você criar uma classe:

Quando você escreve uma definição de classe, por exemplo, como esta,

class Foo(object): 
    'demo'

Você instanciar um objeto da classe.

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

É o mesmo type como funcionalmente chamando com os argumentos apropriados e atribuir o resultado a uma variável com esse nome:

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

Note, algumas coisas automaticamente adicionadas ao __dict__, ou seja, o espaço de nomes:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

O metaclass do objeto que criamos, em ambos os casos, é type.

(A nota lateral sobre o conteúdo do __dict__ classe: __module__ está lá porque as classes devem saber onde eles são definidos, e __dict__ e __weakref__ estão lá porque nós não definem __slots__ - se definir __slots__ vamos economizar um pouco de espaço nos casos, como podemos impedir __dict__ e __weakref__, excluindo-os. por exemplo:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

... Mas estou divagando.)

Podemos estender type como qualquer outra definição de classe:

Aqui está o __repr__ padrão de classes:

>>> Foo
<class '__main__.Foo'>

Uma das coisas mais valiosas que podemos fazer por padrão em escrever um objeto Python é fornecer-lo com uma boa __repr__. Quando chamamos help(repr) aprendemos que há um bom teste para a __repr__ que também requer um teste de igualdade - obj == eval(repr(obj)). A seguir implementação simples de __repr__ e __eq__ para instâncias de classe da nossa classe tipo nos fornece uma demonstração de que pode melhorar na __repr__ padrão de classes:

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True            
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

Agora, quando criamos um objeto com este metaclass, o __repr__ ecoou na linha de comando fornece uma visão muito menos feio do que o padrão:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

Com um __repr__ agradável definido para a instância da classe, temos uma forte capacidade para depurar nosso código. No entanto, muito mais corrente com eval(repr(Class)) é improvável (como funções seria praticamente impossível eval de seu __repr__ padrão do).

Um esperado uso: __prepare__ um namespace

Se, por exemplo, queremos saber em que ordem os métodos de uma classe são criados, nós poderia fornecer uma dict ordenada como o namespace da classe. Gostaríamos de fazer isso com __prepare__ que retorna o dict namespace para o classe, se ele é implementado em Python 3 :

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

E uso:

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

E agora temos um registro da ordem em que esses métodos (e outros atributos de classe) foram criados:

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

Note, este exemplo foi adaptado do documentação - a nova enum na biblioteca padrão faz isso.

Então, o que fizemos foi instanciar um metaclass criando uma classe. Também pode tratar a metaclass como faríamos com qualquer outra classe. Ele tem uma ordem de resolução método:

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

E tem aproximadamente o repr correta (o que não podemos mais eval a menos que possamos encontrar uma maneira de representar as nossas funções.):

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})

atualização Python 3

Existem (neste momento) dois métodos principais em uma metaclasse:

  • __prepare__, e
  • __new__

__prepare__ permite que você fornecer um mapeamento personalizado (como um OrderedDict) para ser usado como espaço de nomes enquanto a classe está sendo criada. Você deve retornar uma instância de qualquer namespace que você escolher. Se você não implementar __prepare__ um dict normal é usada.

__new__ é responsável pela actual de criação / modificação da classe final.

A nu-ossos, não fazer nada extra-metaclass gostaria:

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

Um exemplo simples:

Digamos que você queira algum código de validação simples para rodar em seus atributos - como ele deve ser sempre um int ou um str. Sem uma metaclasse, sua classe seria algo parecido com:

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

Como você pode ver, você tem que repetir o nome do atributo duas vezes. Isso faz com que erros possíveis, juntamente com erros irritantes.

A metaclass simples pode resolver esse problema:

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

Isto é o que a metaclasse seria semelhante (não usando __prepare__ uma vez que não é necessário):

class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

Uma corrida amostra de:

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

produz:

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in <module>
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

Nota :. Este exemplo é bastante simples que também poderia ter sido realizado com um decorador de classe, mas presumivelmente um metaclass real estaria fazendo muito mais

classe A 'ValidateType' para referência:

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value

Papel do método __call__() uma metaclasse ao criar uma instância de classe

Se você já fez de programação Python para mais do que alguns meses você acabará se deparando com código parecido com este:

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

O último é possível quando você implementar o método __call__() mágica na classe.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

O método __call__() é invocado quando um exemplo de uma classe é usado como um exigível. Mas como vimos a partir de respostas anteriores uma classe em si é uma instância de uma metaclasse, por isso, quando usamos a classe como um exigível (ou seja, quando criamos uma instância dele) na verdade estamos chamando seu metaclass' método __call__(). Neste ponto, a maioria dos programadores Python são um pouco confuso, porque eles disseram que ao criar uma instância como esta instance = SomeClass() você está chamando seu método __init__(). Alguns que cavou um pouco mais profundo saber que antes __init__()__new__(). Bem, hoje outra camada de verdade está sendo revelada, antes __new__() há o metaclass __call__().

vamos estudar a cadeia de chamada de método de especificamente a perspectiva de criação de uma instância de uma classe.

Esta é uma metaclasse que registra exatamente o momento antes de uma instância é criada e no momento em que está prestes a devolvê-lo.

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

Esta é uma classe que usa esse metaclass

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

E agora vamos criar uma instância de Class_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

Observe que o código acima na verdade não faz nada mais do que registrando as tarefas. Cada método delega o trabalho real para a implementação do seu pai, mantendo, assim, o comportamento padrão. Desde type é classe pai de Meta_1 (type sendo o metaclass pai padrão) e considerando a seqüência de ordenação da saída acima, agora temos uma pista sobre o que seria a implementação pseudo da type.__call__():

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

Podemos ver que o método metaclass __call__() é o que é chamado pela primeira vez. Em seguida, os delegados criação da instância para o método __new__() da classe e inicialização para __init__() da instância. É também o que em última análise, retorna a instância.

Do exposto resulta que __call__() a metaclasse também é dada a oportunidade de decidir se quer ou não uma chamada para Class_1.__new__() ou Class_1.__init__() acabará por ser feita. Ao longo de sua execução poderia realmente retornar um objeto que não tenha sido tocado por qualquer um destes métodos. Tomemos por exemplo esta abordagem ao padrão Singleton:

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__()
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

Vamos observar o que acontece quando repetidamente tentando criar um objeto do tipo Class_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True

A metaclasse é uma classe que conta como (alguns) outra classe deve ser criado.

Este é um caso onde eu vi metaclass como uma solução para o meu problema: Eu tive um problema muito complicado, que provavelmente poderia ter sido resolvido de forma diferente, mas eu escolhi para resolvê-lo usando uma metaclasse. Por causa da complexidade, é um dos poucos módulos que tenho escrito, onde os comentários no módulo superam a quantidade de código que foi escrito. Aqui está ...

#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls, "destination_path", paths.pop() if paths else None)
            setattr(cls, "source_paths", paths)
            setattr(cls, "options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()

type é realmente um metaclass - uma classe que cria mais classes. A maioria dos metaclass são as subclasses de type. O metaclass recebe a classe new como seu primeiro argumento e fornecer acesso a objeto de classe com detalhes como mencionado abaixo:

>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print ('class name: %s' %name )
...         print ('Defining class %s' %cls)
...         print('Bases %s: ' %bases)
...         print('Attributes')
...         for (name, value) in attrs.items():
...             print ('%s :%r' %(name, value))
... 

>>> class NewClass(object, metaclass=MetaClass):
...    get_choch='dairy'
... 
class name: NewClass
Bases <class 'object'>: 
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

Observe que a classe não foi instanciado a qualquer momento; o simples ato de criar a classe execução desencadeada do metaclass.

O tl; dr versão

A função type(obj) você recebe o tipo de um objeto.

O type() de uma classe é a sua metaclass .

Para usar um metaclass:

class Foo(object):
    __metaclass__ = MyMetaClass
aulas

Python são objetos em si - como no exemplo - de sua meta-classe.

O metaclass padrão, que é aplicado quando quando você determinar aulas como:

class foo:
    ...

classe meta são usados ??para aplicar uma regra a todo um conjunto de classes. Por exemplo, suponha que você está construindo um ORM para acessar um banco de dados, e você quer registros de cada tabela para ser de uma classe mapeada para que a tabela (com base em campos, regras de negócio, etc ..), um possível uso de metaclass é, por exemplo, a lógica pool de conexão, que é social por todas as classes de registro de todas as tabelas. Outro uso é lógica para apoiar as chaves estrangeiras, que envolve múltiplas classes de registros.

quando você define metaclass, você subclasse tipo, e pode overrided os seguintes métodos mágicos para inserir a sua lógica.

class somemeta(type):
    __new__(mcs, name, bases, clsdict):
      """
  mcs: is the base metaclass, in this case type.
  name: name of the new class, as provided by the user.
  bases: tuple of base classes 
  clsdict: a dictionary containing all methods and attributes defined on class

  you must return a class object by invoking the __new__ constructor on the base metaclass. 
 ie: 
    return type.__call__(mcs, name, bases, clsdict).

  in the following case:

  class foo(baseclass):
        __metaclass__ = somemeta

  an_attr = 12

  def bar(self):
      ...

  @classmethod
  def foo(cls):
      ...

      arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}

      you can modify any of these values before passing on to type
      """
      return type.__call__(mcs, name, bases, clsdict)


    def __init__(self, name, bases, clsdict):
      """ 
      called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
      """
      pass


    def __prepare__():
        """
        returns a dict or something that can be used as a namespace.
        the type will then attach methods and attributes from class definition to it.

        call order :

        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__ 
        """
        return dict()

    def mymethod(cls):
        """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
        """
        pass

De qualquer forma, esses dois são os ganchos mais comumente usados. metaclassing é poderoso, e acima está longe lista próximo e exaustiva de usos para metaclassing.

A função type () pode retornar o tipo de um objeto ou criar um novo tipo,

Por exemplo, podemos criar uma classe Oi com a função type () e não precisa usar este caminho com classe Hi (objeto):

def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

Além de usar tipo () para criar classes dinamicamente, você pode controlar o comportamento de criação de classe e uso metaclass.

De acordo com o modelo de objeto Python, a classe é o objeto, de modo que a classe deve ser uma instância de outra certa classe. Por padrão, uma classe Python é instância da classe tipo. Isto é, tipo é metaclass da maior parte do built-in classes e metaclass de classes definidas pelo usuário.

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

Mágica terá efeito quando passamos argumentos nomeados em metaclasse, indica que o interpretador Python para criar o CustomList através ListMetaclass. new (), neste momento, podemos modificar a definição de classe, por exemplo, e adicionar um novo método e, em seguida, retornar a definição revista.

Além das respostas publicadas posso dizer que um metaclass define o comportamento de uma classe. Assim, você pode definir explicitamente o seu metaclass. Sempre Python recebe um class palavra-chave, em seguida, ele começa a procurar o metaclass. Se não for encontrado - o tipo metaclass padrão é usado para criar o objeto da classe. Usando o atributo __metaclass__, você pode definir metaclass de sua classe:

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

Ela vai produzir a saída como esta:

class 'type'

E, claro, você pode criar seu próprio metaclass para definir o comportamento de qualquer classe que são criados usando sua classe.

Para fazer isso, sua classe tipo metaclass padrão deve ser herdada como esta é a principal metaclass:

class MyMetaClass(type):
   __metaclass__ = type
   # you can write here any behaviour you want

class MyTestClass:
   __metaclass__ = MyMetaClass

Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

A saída será:

class '__main__.MyMetaClass'
class 'type'
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top