Existe alguma razão para escolher __new__ sobre __init__ ao definir um metaclass?
-
12-09-2019 - |
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.
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__
.