定义元类时是否有理由选择 __new__ 而不是 __init__ ?
我总是设置这样的元类:
class SomeMetaClass(type):
def __new__(cls, name, bases, dict):
#do stuff here
但我刚刚遇到一个定义如下的元类:
class SomeMetaClass(type):
def __init__(self, name, bases, dict):
#do stuff here
有什么理由比另一个更喜欢其中一个?
更新:请记住,我正在询问有关在元类中使用 __new__
和 __init__
的问题。我在另一堂课上已经明白了他们之间的区别。但在元类中,我无法使用 __new__ 来实现缓存,因为仅在元类中创建类时调用 __new__ 。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果你想在创建类之前改变属性字典,或者改变基元组,你必须使用
__new__
。当 __init__ 看到参数时,类对象已经存在。另外,如果您想返回除相关类型的新创建的类之外的其他内容,则必须使用 __new__ 。另一方面,当
__init__
运行时,该类确实存在。因此,您可以执行诸如将对刚刚创建的类的引用提供给其成员对象之一之类的操作。编辑:更改措辞以使其更清楚,“对象”是指类对象。
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.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.Edit: changed wording to make it more clear that by "object", I mean class-object.
事实上,有几个区别。
一方面,
__new__
和__init__
中的第一个参数并不相同,每个坚持只使用cls
的人都没有明确表示这一点。两种情况(尽管变量名称不具有任何特定含义)。有人指出了这一点,这是理解差异的核心:__new__
获取 metaclass -MyType
(记住应用程序 -级别类尚未创建)。您可以在此处更改base
(如果您不小心,可能会导致 MRO 解析错误)。我将调用该变量mcls
,以区别于通常引用应用程序级类的cls
。__init__
获取新创建的应用程序级 class、Bar
和Foo
,到那时,此类的命名空间已填充,请参阅下面示例中的 cls_attrib 。按照通常的命名约定,我将坚持使用cls
。示例代码:
Several differences, in fact.
For one thing, the first argument in
__new__
and__init__
are not the same, which is not made clear by everyone insisting on just usingcls
in both cases (despite the fact that the variable name doesn't hold any particular meaning). Someone pointed this out and it's core to understanding the difference:__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). I'll call that variablemcls
, to differentiate it from the usualcls
referring to application level class.__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. I'll stick tocls
as per usual naming convention.Sample code:
output:
您可以在官方文档中看到完整的文章,但基本上,< code>__new__ 在新对象创建之前被调用(为了创建它),
__init__
在之后被调用创建新对象(为了初始化它)。使用 __new__ 允许使用诸如对象缓存(总是为相同的参数返回相同的对象而不是创建新的对象)或生成与请求的不同类的对象(有时用于返回更具体的子类)之类的技巧。要求的课程)。一般来说,除非你正在做一些非常奇怪的事情,否则
__new__
的实用性有限。如果您不需要调用此类技巧,请坚持使用__init__
。You can see the full writeup in the official docs, but basically,
__new__
is called before the new object is created (for the purpose of creating it) and__init__
is called after the new object is created (for the purpose of initializing it).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__ 中进行。类的名称也是如此,但它似乎有一个特殊之处。当您更改
name
时,它不会传播到__init__
,尽管attr
是这样。所以你会得到:
prints
如果
__init__
调用一个超级元类来执行一些额外的魔法,那么了解这一点很重要。在这种情况下,在调用 super.__init__ 之前必须再次更改名称。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 thename
of the class but there seems to be a peculiarity with it. When you changename
, it is not propagated to__init__
, even though, for exampleattr
is.So you’ll have:
prints
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 callingsuper.__init__
.您可以实施缓存。
Person("Jack")
在第二个示例中始终返回一个新对象,而您可以在第一个示例中使用__new__
查找现有实例(或者如果您想要,则不返回任何内容) )。You can implement caching.
Person("Jack")
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).