python 在定义元类时,是否有任何理由选择 __new__ 而不是 __init__?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1840421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there any reason to choose __new__ over __init__ when defining a metaclass?
提问by Jason Baker
I've always set up metaclasses something like this:
我总是像这样设置元类:
class SomeMetaClass(type):
def __new__(cls, name, bases, dict):
#do stuff here
But I just came across a metaclass that was defined like this:
但我刚刚遇到了一个定义如下的元类:
class SomeMetaClass(type):
def __init__(self, name, bases, dict):
#do stuff here
Is there any reason to prefer one over the other?
有什么理由更喜欢一个吗?
Update: Bear in mind that I'm asking about using __new__
and __init__
in a metaclass. I already understand the difference between them in another class. But in a metaclass, I can't use __new__
to implement caching because __new__
is only called upon class creation in a metaclass.
更新:请记住,我正在询问在元类中使用__new__
和__init__
。我已经明白他们在另一堂课上的区别。但是在元类中,我不能__new__
用来实现缓存,因为__new__
只在元类中调用类创建。
回答by Matt Anderson
If you want to alter the attributes dict before the class is created, or change the bases tuple, you have to use __new__
. By the time __init__
sees the arguments, the class object already exists. Also, you have to use __new__
if you want to return something other than a newly created class of the type in question.
如果要在创建类之前更改属性 dict,或更改基元组,则必须使用__new__
. 当__init__
看到参数时,类对象已经存在。此外,__new__
如果要返回相关类型的新创建类以外的其他内容,则必须使用。
On the other hand, by the time __init__
runs, the class does exist. Thus, you can do things like give a reference to the just-created class to one of its member objects.
另一方面,随着时间的推移__init__
,该类确实存在。因此,您可以执行一些操作,例如将刚刚创建的类的引用提供给其成员对象之一。
Edit: changed wording to make it more clear that by "object", I mean class-object.
编辑:更改措辞以使其更清楚,“对象”是指类对象。
回答by Ben Blank
You can see the full writeup in the official docs, but basically, __new__
is called beforethe new object is created (for the purpose of creating it) and __init__
is called afterthe new object is created (for the purpose of initializing it).
您可以在官方文档中看到完整的说明,但基本上,在创建新对象之前__new__
调用(为了创建它)并在创建新对象之后调用(为了初始化它)。__init__
Using __new__
allows tricks like object caching (always returning the same object for the same arguments rather than creating new ones) or producing objects of a different class than requested (sometimes used to return more-specific subclasses of the requested class). Generally, unless you're doing something pretty odd, __new__
is of limited utility. If you don't need to invoke such trickery, stick with __init__
.
使用__new__
允许诸如对象缓存(总是为相同的参数返回相同的对象而不是创建新的对象)或生成与请求的类不同的对象(有时用于返回所请求类的更具体的子类)之类的技巧。通常,除非您正在做一些非常奇怪的事情,否则__new__
实用性有限。如果您不需要调用此类技巧,请坚持使用__init__
.
回答by Debilski
As has been said, if you intend to alter something like the base classes or the attributes, you'll have to do it in __new__
. The same is true for the name
of the class but there seems to be a peculiarity with it. When you change name
, it is not propagated to __init__
, even though, for example attr
is.
如前所述,如果您打算更改基类或属性之类的内容,则必须在__new__
. 类的也是如此,name
但它似乎有一个特点。当您更改 时name
,它不会传播到__init__
,即使例如attr
是。
So you'll have:
所以你会有:
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__
prints
印刷
I am still called 'A' in init
Now I'm A_class_named_A
This is important to know, if __init__
calls a super metaclass which does some additional magic. In that case, one has to change the name again before calling super.__init__
.
知道这一点很重要,如果__init__
调用一个超级元类,它会产生一些额外的魔法。在这种情况下,必须在调用 之前再次更改名称super.__init__
。
回答by JL Peyret
Several differences, in fact.
事实上,有几个不同之处。
For one thing, the first argument in __new__
and __init__
are not the same, which isn't helped by everyone just using, cls
. Someone pointed this out and it's core to understanding the difference:
一方面,__new__
和__init__
中的第一个参数不一样,每个人都只使用 , 并没有帮助cls
。有人指出了这一点,这是理解差异的核心:
__new__
gets the metaclass-MyType
in my example (remember the application-level class is not created yet). This is where you can alterbases
(which can cause MRO resolution errors if you're not careful).__init__
gets the newly-created application-level class,Bar
andFoo
and, by that time, this class's namespace has been populated, seecls_attrib
in example below.
__new__
获取元类-MyType
在我的示例中(请记住尚未创建应用程序级类)。这是您可以更改的地方bases
(如果您不小心,可能会导致 MRO 解析错误)。__init__
获取新创建的应用程序级class,Bar
并且Foo
到那时,该类的命名空间已填充,请参见cls_attrib
下面的示例。
Sample code:
示例代码:
class Mixin:
pass
class MyType(type):
def __new__(mcls, name, bases, attrs, **kwargs):
print(" MyType.__new__.mcls:%s" % (mcls))
if not Mixin in bases:
#could cause MRO resolution issues, but if you want to alter the bases
#do it here
bases += (Mixin,)
#The call to super.__new__ can also modify behavior:
# classes Foo and Bar are instances of MyType
return super(MyType, mcls).__new__(mcls, name, bases, attrs)
#now we're back to the standard `type`
#doing this will neuter most of the metaclass behavior, __init__ wont
#be called.
#return super(MyType, mcls).__new__(type, name, bases, attrs)
def __init__(cls, name, bases, attrs):
print(" MyType.__init__.cls:%s." % (cls))
#I can see attributes on Foo and Bar's namespaces
print(" %s.cls_attrib:%s" % (cls.__name__, getattr(cls, "cls_attrib", None)))
return super().__init__(name, bases, attrs)
print("\n Foo class creation:")
class Foo(metaclass=MyType):
pass
print("\n bar class creation:")
class Bar(Foo):
#MyType.__init__ will see this on Bar's namespace
cls_attrib = "some class attribute"
output:
输出:
Foo class creation:
MyType.__new__.mcls:<class '__main__.test.<locals>.MyType'>
MyType.__init__.cls:<class '__main__.test.<locals>.Foo'>.
Foo.cls_attrib:None
Bar class creation:
MyType.__new__.mcls:<class '__main__.test.<locals>.MyType'>
MyType.__init__.cls:<class '__main__.test.<locals>.Bar'>.
Bar.cls_attrib:some class attribute
回答by Otto Allmendinger
You can implement caching. Person("Hyman")
always returns a new object in the second example while you can lookup an existing instance in the first example with __new__
(or not return anything if you want).
您可以实现缓存。Person("Hyman")
在第二个示例中始终返回一个新对象,而您可以在第一个示例中查找现有实例__new__
(如果需要,也可以不返回任何内容)。