Pergunta

Eu sempre configurar metaclasses algo como isto:

class SomeMetaClass(type):
    def __new__(cls, name, bases, dict):
        #do stuff here

Mas eu só vim através de uma metaclasse que foi definido como este:

class SomeMetaClass(type):
    def __init__(self, name, bases, dict):
        #do stuff here

Existe alguma razão para preferir um sobre o outro?

Atualizar : Tenha em mente que eu estou perguntando sobre o uso __new__ e __init__ em uma metaclasse. Eu já entendem a diferença entre eles em outra classe. Mas, em uma metaclasse, eu não posso usar __new__ implementar cache porque __new__ só é chamado a criação da classe em uma metaclasse.

Foi útil?

Solução

Se você quiser alterar o dict atributos antes que a classe é criada, ou alterar a tupla bases, você tem que usar __new__. Pela __init__ tempo vê os argumentos, o objeto de classe já existe. Além disso, você tem que usar __new__ se você quiser retornar algo diferente de uma classe recém-criada do tipo em questão.

Por outro lado, no momento em __init__ corridas, a classe não existe. Assim, você pode fazer coisas como dar uma referência para a classe recém-criado para um de seus objetos membros.

Editar :. Mudado redação para torná-lo mais claro que por "objeto", quero dizer class-objeto

Outras dicas

Você pode ver o writeup completa em os documentos oficiais , mas, basicamente, __new__ é chamado antes o novo objeto é criado (com a finalidade de criá-la) e __init__ é chamado após o novo objeto é criado (com a finalidade de inicializar -lo).

Usando __new__ permite truques como cache de objetos (sempre retornar o mesmo objeto para os mesmos argumentos em vez de criar novos) ou produzir objetos de uma classe diferente do solicitado (por vezes usada para retornar subclasses mais específicas da classe solicitada). Geralmente, a menos que você está fazendo algo muito estranho, __new__ é de utilidade limitada. Se você não precisa de invocar tais artifícios, vara com __init__.

Você pode implementar cache. Person("Jack") sempre retorna um novo objeto no segundo exemplo, enquanto você pode consultar uma instância existente no primeiro exemplo com __new__ (ou não retornar nada se você quiser).

Como já foi dito, se você pretende algo alter como as classes de base ou os atributos, você terá que fazê-lo em __new__. O mesmo é verdadeiro para o name da classe, mas parece haver uma peculiaridade com ele. Quando você altera name, não é propagada para __init__, apesar de, por exemplo attr é.

Então você vai ter:

class Meta(type):
    def __new__(cls, name, bases, attr):
        name = "A_class_named_" + name
        return type.__new__(cls, name, bases, attr)

    def __init__(cls, name, bases, attr):
        print "I am still called '" + name + "' in init"
        return super(Meta, cls).__init__(name, bases, attr)

class A(object):
    __metaclass__ = Meta

print "Now I'm", A.__name__

impressões

I am still called 'A' in init
Now I'm A_class_named_A

Isso é importante saber, se __init__ chama de metaclass super que faz um pouco de magia adicional. Nesse caso, é preciso mudar o nome novamente antes de chamar super.__init__.

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