Quais são metaclasses em Python?
-
01-07-2019 - |
Pergunta
O que são metaclasses e o que vamos usá-los para?
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:
- macaco patching
- decoradores de classe
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 ??strong>:. 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__()
há __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
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'