Pergunta

Eu tenho um amigo que gosta de usar metaclasses, e regularmente lhes oferece como uma solução.

Eu sou da mente que você quase nunca precisa usar metaclasses. Por quê? porque eu acho que se você está fazendo algo parecido a uma classe, você provavelmente deve estar fazendo isso para um objeto. E um pequeno redesenho / refactor está em ordem.

Ser capaz de utilizar metaclasses causou um monte de gente em um monte de lugares para usar classes como uma espécie de segundo objeto taxa, que apenas parece desastrosa para mim. Está programando para ser substituído por meta-programação? A adição de decoradores de classe, infelizmente, tornou ainda mais aceitável.

Então, por favor, estou desesperado para saber suas válidos (concreto) casos de uso para metaclasses em Python. Ou para ser esclarecido por que motivo mutação classes é melhor do que mutação objetos, às vezes.

Vou começar:

Às vezes, quando se utiliza um terceiro biblioteca é útil para ser capaz de mutar a classe de uma determinada maneira.

(Este é o único caso que eu posso pensar, e não é concreto)

Foi útil?

Solução

Eu tenho uma classe que lida com plotagem não-interativo, como um frontend para Matplotlib. No entanto, de vez em quando se quer fazer plotagem interativo. Com apenas um par de funções que eu descobri que eu era capaz de incrementar a contagem de figura, chamada de desenho manualmente, etc, mas eu precisava fazer estes antes e depois de cada chamada plotagem. Portanto, para criar tanto um invólucro plotagem interativo e um invólucro plotagem fora da tela, eu achei que era mais eficiente de fazer isso via metaclasses, envolvendo os métodos apropriados, do que fazer algo como:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

Este método não acompanhar as mudanças de API e assim por diante, mas que itera sobre a classe atributos em __init__ antes de re-definir os atributos da classe é mais eficiente e mantém as coisas até à data:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

É claro, pode haver melhores maneiras de fazer isso, mas eu descobri que isso seja eficaz. Claro, isso também pode ser feito em __new__ ou __init__, mas esta foi a solução que eu encontrei o mais simples.

Outras dicas

Eu estava a mesma pergunta recentemente, e veio com várias respostas. Espero que seja OK para reviver esta discussão, como eu queria para a reflexão sobre alguns dos casos de uso mencionados, e adicionar alguns novos.

A maioria das metaclasses que eu vi fazer uma de duas coisas:

  1. Registro (adicionando uma classe para uma estrutura de dados):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Sempre que você subclasse Model, sua classe é registrado no dicionário models:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    Isto pode também ser feito com decoradores de classe:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    Ou com uma função de registro explícito:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    Na verdade, este é praticamente o mesmo:. Você menciona classe decoradores desfavoravelmente, mas é realmente nada mais do que o açúcar sintático para uma chamada de função em uma classe, então não há nenhuma mágica nisso

    De qualquer forma, a vantagem de metaclasses neste caso é herança, como eles trabalham para quaisquer subclasses, enquanto que as outras soluções só funcionam para subclasses explicitamente decorados ou registados.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. Refactoring (modificar atributos de classe ou adicionar novos):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Sempre que você subclasse Model e definir alguns atributos Field, eles são injetados com seus nomes (para mensagens de erro mais informativas, por exemplo), e agrupados em um dicionário _fields (para fácil iteração, sem ter que olhar através de todos os atributos de classe e todas as suas classes base atributos de cada vez):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    Mais uma vez, isso pode ser feito (sem herança) com um decorador de classe:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    ou explicitamente:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    Embora, ao contrário de sua defesa para programação não-meta legível e de fácil manutenção, isso é muito mais complicado, redundante e propensa a erros:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

Tendo considerado o mais comum e casos de uso concretos, os únicos casos em que seja absolutamente necessário metaclasses uso é quando você deseja modificar o nome ou lista de classes base classe, porque uma vez definidos, estes parâmetros são assados ??na classe, e nenhum decorador ou função pode unbake-los.

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

Isto pode ser útil em estruturas para emitir avisos sempre que classes com nomes semelhantes ou árvores de herança incompletos são definidos, mas eu não posso pensar em uma razão ao lado de corrico para realmente mudar esses valores. Talvez David Beazley pode.

De qualquer forma, em Python 3, metaclasses também têm o método __prepare__, que permite avaliar o corpo da classe em um mapeamento diferente de um dict, apoiando assim atributos ordenados, atributos sobrecarregados, e outras coisas legal mau:

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

Você pode argumentar ordenou atributos podem ser alcançados com contadores de criação, e sobrecarga pode ser simulado com argumentos padrão:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

Além de ser muito mais feia, também é menos flexível: o que se quer atributos literais ordenados, como inteiros e strings? E se None é um valor válido para x?

Aqui está uma forma criativa para resolver o primeiro problema:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

E aqui está uma forma criativa para resolver o segundo:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

Mas isso é muito, muito voodoo-er que um metaclass simples (especialmente o primeiro, o que realmente derrete seu cérebro). Meu ponto é, você olha para metaclasses como desconhecido e contra-intuitivo, mas você também pode olhar para eles como o próximo passo da evolução em linguagens de programação: você só tem que ajustar a sua mentalidade. Afinal, você provavelmente poderia fazer tudo em C, incluindo a definição de uma estrutura com ponteiros de função e passando-o como o primeiro argumento para suas funções. Uma pessoa que vê C ++ pela primeira vez pode dizer ", o que é essa mágica? Por que o compilador passando implicitamente this Para métodos, mas não para funções regulares e estáticos? É melhor ser explícito e detalhado sobre os seus argumentos" Mas então, programação orientada a objetos é muito mais poderoso uma vez que você obtê-lo;. E assim é esta, uh ... programação quasi-orientada a aspectos, eu acho E uma vez que você. entender metaclasses, eles são realmente muito simples, então por que não usá-los quando conveniente?

E, finalmente, metaclasses são rad, ea programação deve ser divertido. Utilizando construções de programação padrão e padrões de projeto o tempo todo é chato e sem inspiração, e dificulta a sua imaginação. Viva um pouco! Aqui está uma metametaclass, só para você.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

O objetivo da metaclasses não é substituir a classe / objeto distinção com metaclass / aula - é para alterar o comportamento de definições de classe (e, portanto, as suas instâncias) de alguma forma. Efetivamente é para alterar o comportamento da declaração de classe de maneiras que podem ser mais útil para o seu domínio particular do que o padrão. As coisas que tenho usado-los para são:

  • Como acompanhar subclasses, geralmente para registrar manipuladores. Isso é útil quando se usa uma configuração de estilo plug-in, onde você deseja registrar um manipulador para uma coisa particular simplesmente por subclasse e criação de alguns atributos de classe. por exemplo. suponha que você escrever um manipulador para vários formatos de música, onde os métodos de cada classe implementa apropriadas (play / obter etiquetas etc) para o seu tipo. Adicionando um manipulador para um novo tipo torna-se:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    O metaclass seguida, mantém um dicionário de {'.mp3' : MP3File, ... } etc, e constrói um objeto do tipo apropriado quando você solicitar um manipulador através de uma função de fábrica.

  • Mudar o comportamento. Você pode querer adicionar um significado especial para certos atributos, resultando em comportamento alterado quando eles estão presentes. Por exemplo, você pode querer olhar para métodos com o _get_foo nome e _set_foo e transparente convertê-los em propriedades. Como um exemplo do mundo real, aqui está uma receita que eu escrevi para dar mais C-como definições de struct . A metaclasse é usada para converter os itens declarados em uma string formato struct, manipulação de herança etc, e produzir uma classe capaz de lidar com ele.

    Para outros exemplos do mundo real, dê uma olhada em vários ORMs, como de sqlalchemy ORM ou SQLObject . Novamente, o objetivo é interpretar defintions (aqui definições de coluna SQL) com um significado particular.

Vamos começar com citações clássicas de Tim Peter:

Metaclasses são mágicos mais profundo do que 99% dos usuários nunca deve se preocupar. E se você quer saber se você precisar deles, você não (as pessoas que realmente precisam eles saibam com certeza que eles precisar deles, e não precisa de um explicação sobre o porquê). Tim Peters (C.l.p pós 2002/12/22)

Dito isto, tenho (periodicamente) deparar verdadeiros usos de metaclasses. O que vem à mente é em Django onde todos os seus modelos herdam models.Model. models.Model, por sua vez, faz alguma mágica grave para envolver seus modelos DB com bondade ORM do Django. Essa mágica acontece por meio de metaclasses. Ele cria todos os tipos de classes de exceção, aulas de gerente, etc etc.

Veja django / db / modelos / base.py, ModelBase classe () para o início da história.

Metaclasses pode ser útil para a construção de domínio específico Línguas em Python. Exemplos concretos são o Django, sintaxe declarativa 's SQLObject de esquemas de base de dados.

Um exemplo básico de Um conservador metaclass por Ian Bicking:

As metaclasses que usei foram principalmente para apoiar uma espécie de estilo declarativo de programação. Para exemplo, considere uma validação schema:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

Algumas outras técnicas: Ingredientes para a construção de uma DSL em Python (pdf).

Edit (por Ali): Um exemplo de fazer isso usando coleções e instâncias é o que eu prefiro. O fato importante é as instâncias, que lhe dão mais poder, e eliminar a razão para metaclasses uso. Além disso pena notar que o seu exemplo usa uma mistura de classes e instâncias, que é certamente uma indicação de que você não pode simplesmente fazer tudo com metaclasses. E cria uma forma verdadeiramente não-uniforme de fazê-lo.

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

Não é perfeito, mas já há quase zero mágica, não há necessidade de metaclasses e melhor uniformidade.

Um teste padrão razoável de utilização metaclass está fazendo algo uma vez, quando uma classe é definida em vez de repetidamente sempre que a mesma classe é instanciado.

Quando várias classes compartilham o mesmo comportamento especial, repetindo __metaclass__=X é obviamente melhor do que repetir o código de propósito específico e / ou introduzindo superclasses ad-hoc compartilhado.

Mas, mesmo com apenas uma classe especial e nenhuma extensão previsível, __new__ e __init__ de uma metaclasse são uma maneira mais limpa para inicializar variáveis ??de classe ou outros dados globais do que misturando código special-purpose e def e class declarações normais no corpo definição de classe .

A única vez que eu usei metaclasses em Python foi ao escrever um wrapper para a API do Flickr.

Meu objetivo era raspar do flickr site da API e gerar dinamicamente uma hierarquia de classe completo para permitir acesso à API usando Python objetos:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

Então, nesse exemplo, porque eu gerado toda a API Python Flickr a partir do site, eu realmente não sei as definições de classe em tempo de execução. Ser capaz de gerar dinamicamente tipos foi muito útil.

Eu estava pensando a mesma coisa ontem e concordo completamente. As complicações no código causados ??por tentativas de torná-lo mais declarativa geralmente fazem a base de código mais difícil de manter, mais difícil de ler e menos pythônico na minha opinião. Ele também normalmente requer muita copy.copy () ing (para manter a herança e para copiar de classe para exemplo) e significa que você tem que olhar em muitos lugares para ver o que está acontecendo (sempre à procura de metaclass up) que vai contra a grãos python também. Tenho vindo a escolher através FormEncode e sqlalchemy código para ver se um estilo tão declarativa valeu a pena e sua claramente não. Tal estilo deve ser deixada para descritores (como a propriedade e métodos) e os dados imutáveis. Ruby tem melhor suporte para tais estilos declarativas e estou feliz a linguagem python núcleo não vai por esse caminho.

Eu posso ver o seu uso para depuração, adicionar um metaclass a todas as suas classes base para obter informações mais ricos. Vejo também o seu uso apenas em (muito) grandes projetos para se livrar de algum código clichê (mas com a perda de clareza). sqlalchemy para exemplo faz usá-los em outro lugar , para adicionar um método personalizado especial a todas as subclasses com base em um valor de atributo em sua definição de classe por exemplo um exemplo de brinquedo

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

poderia ter uma metaclasse que gerou um método em que a classe com propriedades especiais com base em "Olá" (digamos, um método que acrescentou: "Olá" ao final de uma string). Poderia ser bom para manutenção para se certificar de que você não tem que escrever um método em cada subclasse que você faz, em vez tudo o que você tem que definir é method_maker_value.

A necessidade para isso é que tão raro e só reduz um pouco de digitação que não é realmente vale a pena considerar a menos que você tem uma base de código grande o suficiente.

Você nunca absolutamente necessidade usar uma metaclasse, desde que você sempre pode construir uma classe que faz o que quiser usando herança ou agregação da classe que você deseja modificar.

Dito isso, ele pode ser muito útil em Smalltalk e Ruby para ser capaz de modificar uma classe existente, mas Python não gosta de fazer isso diretamente.

Há uma excelente DeveloperWorks artigo em metaclassing em Python que ajuda poder. A Wikipedia artigo também é muito bom.

Metaclasses não estão substituindo a programação! Eles são apenas um truque que pode automatizar ou tornar mais elegantes algumas tarefas. Um bom exemplo disso é Pygments destaque de sintaxe biblioteca. Ele tem uma classe chamada RegexLexer que permite ao usuário definir um conjunto de regras lexing como expressões regulares sobre uma classe. A metaclasse é usada para transformar as definições em um analisador útil.

Eles são como sal; é fácil de usar muito.

A maneira que eu usei metaclasses era fornecer alguns atributos a classes. Tomemos por exemplo:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

colocará o nome atributo em cada classe que terá o conjunto metaclass para apontar para NameClass.

Algumas bibliotecas GUI ter problemas quando vários segmentos tentar interagir com eles. tkinter é um exemplo; e enquanto um pode lidar explicitamente o problema com eventos e filas, pode ser muito mais simples para usar a biblioteca de uma maneira que ignora o problema completamente. Eis -. A magia de metaclasses

Ser capaz de reescrever dinamicamente uma biblioteca inteira perfeitamente para que ele funcione adequadamente como esperado em um aplicativo com vários segmentos pode ser extremamente útil em algumas circunstâncias. A safetkinter módulo faz isso com a ajuda de uma metaclasse fornecido pelo threadbox módulo - eventos e filas não necessário

Um aspecto elegante de threadbox é que ele não se importa que classe ele clones. Ele fornece um exemplo de como todas as classes base pode ser tocado por um metaclass se necessário. Um benefício adicional que vem com metaclasses é que eles são executados em classes que herdam também. Programas que se escrevem - por que não?

O caso de uso única legítima de uma metaclasse é manter outros desenvolvedores intrometidos de tocar seu código. Uma vez que um arrumam desenvolvedor mestres metaclasses e começa a picar ao redor com o seu, jogar em outro nível ou dois para mantê-los fora. Se isso não funcionar, começar a usar type.__new__ ou talvez algum esquema usando um metaclass recursiva.

(língua escrita no rosto, mas eu já vi este tipo de ofuscação feito. Django é um exemplo perfeito)

Este é um uso menor, mas ... uma coisa que eu encontrei metaclasses útil para é para chamar uma função sempre que uma subclasse é criado. I codificada isso em uma metaclasse que procura um atributo __initsubclass__: sempre que uma subclasse é criado, todas as classes pai que definem esse método são invocados com __initsubclass__(cls, subcls). Isso permite a criação de uma classe pai que, em seguida, registra todas as subclasses com algum registro global, corre cheques invariantes em subclasses sempre que forem definidos, executar ligação tardia operações, etc ... tudo sem ter de funções chamada manualmente ou para criar metaclasses personalizados que executam cada uma dessas tarefas separadas.

Mind você, eu vim lentamente a perceber a magicalness implícita deste comportamento é um pouco indesejável, já que é inesperado se olhando para uma definição de classe fora de contexto ... e assim eu mudei longe de usar essa solução para nada grave, além de inicializar um atributo __super para cada classe e instância.

Recentemente, tive de usar uma metaclasse a ajuda declarativamente definir um modelo SQLAlchemy em torno de uma tabela de banco preenchida com dados do Censo dos EUA de http://census.ire.org/data/bulkdata.html

IRE fornece conchas de banco de dados para as tabelas de dados do censo, que criam inteiro colunas na sequência de uma convenção de nomenclatura do gabinete de recenseamento de p012015, p012016, p012017, etc.

Eu queria a) ser capaz de acessar essas colunas usando uma sintaxe model_instance.p012017, b) ser bastante explícito sobre o que eu estava fazendo e c) não tem que definir explicitamente dezenas de campos no modelo, então eu subclasse DeclarativeMeta de SQLAlchemy para iterate através de uma série de colunas e criar automaticamente campos de modelo correspondentes às colunas:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

Eu poderia, então, usar este metaclass para minha definição do modelo e acesso aos campos enumerados automaticamente no modelo:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...

Parece haver um uso legítimo descrito aqui - Reescrevendo Python docstrings com uma metaclasse

.

Eu tive que usá-los uma vez por um analisador binário para torná-lo mais fácil de usar. Você define uma classe de mensagem com atributos do campo presente no fio. Eles precisavam ser encomendados na forma como eles foram declarados para construir o fio formato final a partir dele. Você pode fazer isso com metaclasses, se você usar um dict namespace ordenada. Na verdade, a sua nos exemplos para Metaclasses:

https://docs.python.org/3/reference/ datamodel.html # metaclass-exemplo

Mas, em geral:. Muito avaliar cuidadosamente, se você realmente precisa da complexidade adicional de metaclasses

a resposta da @ Dan Gittik é legal

os exemplos no final poderia esclarecer muitas coisas, eu mudei para python 3 e dar alguma explicação:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan

  • tudo é objeto, então classe é objeto
  • objeto de classe é criado por metaclass
  • todos os tipos inheritted de tipo é metaclass
  • metaclass poderia controlar classe criando
  • metaclass poderia controlar metaclass criando também (por isso poderia loop para sempre)
  • este é metaprograming ... você poderia controlar o sistema de tipos em tempo de execução
  • novamente, tudo é objeto, este é um sistema uniforme, escreva criar tipo, e digite criar instância

Outro caso de uso é quando você quer ser capaz de modificar atributos de nível de classe e ter certeza que ele só afeta o objeto na mão. Na prática, isso implica "fundir" as fases de metaclasses e classes instantiations, assim, levando-o a lidar apenas com instâncias de classe de seu próprio (único) tipo.

Eu também tive que fazer isso quando (por preocupações de readibility polimorfismo ) queríamos definir dinamicamente property s que voltou resultado valores (podem) a partir de cálculos baseados em ( muitas vezes mudando) atributos de nível de instância, que só pode ser feito no nível da classe , ie após a instanciação metaclass e antes de a instanciação de classe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top