Python(和 Python C API):__new__ 与 __init__

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4859129/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:51:41  来源:igfitidea点击:

Python (and Python C API): __new__ versus __init__

pythoncpython-c-api

提问by Channel72

The question I'm about to ask seems to be a duplicate of Python's use of __new__ and __init__?, but regardless, it's still unclear to me exactly what the practical difference between __new__and __init__is.

我要问的问题似乎是Python 使用 __new__ 和 __init__的重复,但无论如何,我仍然不清楚__new__和之间的实际区别是什么__init__

Before you rush to tell me that __new__is for creating objects and __init__is for initializing objects, let me be clear: I get that.In fact, that distinction is quite natural to me, since I have experience in C++ where we have placement new, which similarly separates object allocation from initialization.

在你急于告诉我这__new__是用于创建对象和__init__用于初始化对象之前,让我明确一点:我明白了。事实上,这种区别对我来说很自然,因为我在 C++ 中有经验,我们有放置 new,它类似地将对象分配与初始化分开。

The Python C API tutorialexplains it like this:

Python的C API教程解释它是这样的:

The new member is responsible for creating (as opposed to initializing) objects of the type. It is exposed in Python as the __new__()method. ... One reason to implement a new method is to assure the initial values of instance variables.

新成员负责创建(而不是初始化)该类型的对象。它在 Python 中作为__new__()方法公开。... 实现新方法的一个原因是确保实例变量的初始值

So, yeah - I getwhat __new__does, but despite this, I stilldon't understand why it's useful in Python. The example given says that __new__might be useful if you want to "assure the initial values of instance variables". Well, isn't that exactly what __init__will do?

所以,是的 - 我明白__new__,但尽管如此,我仍然不明白为什么它在 Python 中很有用。给出的示例表示,__new__如果您想“确保实例变量的初始值” ,这可能很有用。那么,这不正是__init__会做的吗?

In the C API tutorial, an example is shown where a new Type (called a "Noddy") is created, and the Type's __new__function is defined. The Noddy type contains a string member called first, and this string member is initialized to an empty string like so:

在 C API 教程中,显示​​了一个示例,其中创建了一个新类型(称为“Noddy”),并__new__定义了该类型的函数。Noddy 类型包含一个名为 的字符串成员first,该字符串成员被初始化为一个空字符串,如下所示:

static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    .....

    self->first = PyString_FromString("");
    if (self->first == NULL)
    {
       Py_DECREF(self);
       return NULL;
    }

    .....
}

Note that without the __new__method defined here, we'd have to use PyType_GenericNew, which simply initializes all of the instance variable members to NULL. So the only benefit of the __new__method is that the instance variable will start out as an empty string, as opposed to NULL. But why is this ever useful, since if we cared about making sure our instance variables are initialized to some default value, we could have just done that in the __init__method?

请注意,如果没有__new__此处定义的方法,我们必须使用PyType_GenericNew,它只是将所有实例变量成员初始化为 NULL。因此,该__new__方法的唯一好处是实例变量将以空字符串开始,而不是 NULL。 但是为什么这总是有用的,因为如果我们关心确保我们的实例变量被初始化为某个默认值,我们可以在__init__方法中完成它?

采纳答案by ncoghlan

The difference mainly arises with mutable vs immutable types.

差异主要出现在可变和不可变类型上。

__new__accepts a typeas the first argument, and (usually) returns a new instance of that type. Thus it is suitable for use with both mutable and immutable types.

__new__接受一个类型作为第一个参数,并且(通常)返回该类型的一个新实例。因此它适用于可变和不可变类型。

__init__accepts an instanceas the first argument and modifies the attributes of that instance. This is inappropriate for an immutable type, as it would allow them to be modified after creation by calling obj.__init__(*args).

__init__接受一个实例作为第一个参数并修改该实例的属性。这对于不可变类型是不合适的,因为它允许在创建后通过调用obj.__init__(*args).

Compare the behaviour of tupleand list:

比较的行为tuplelist

>>> x = (1, 2)
>>> x
(1, 2)
>>> x.__init__([3, 4])
>>> x # tuple.__init__ does nothing
(1, 2)
>>> y = [1, 2]
>>> y
[1, 2]
>>> y.__init__([3, 4])
>>> y # list.__init__ reinitialises the object
[3, 4]

As to why they're separate (aside from simple historical reasons): __new__methods require a bunch of boilerplate to get right (the initial object creation, and then remembering to return the object at the end). __init__methods, by contrast, are dead simple, since you just set whatever attributes you need to set.

至于为什么它们是分开的(除了简单的历史原因):__new__方法需要一堆样板才能正确(初始对象创建,然后记住最后返回对象)。__init__相比之下,方法非常简单,因为您只需设置需要设置的任何属性。

Aside from __init__methods being easier to write, and the mutable vs immutable distinction noted above, the separation can also be exploited to make calling the parent class __init__in subclasses optional by setting up any absolutely required instance invariants in __new__. This is generally a dubious practice though - it's usually clearer to just call the parent class __init__methods as necessary.

除了__init__方法更容易编写,以及上面提到的可变与不可变的区别之外,还可以利用这种分离,__init__通过在__new__. 不过,这通常是一种可疑的做法 - 通常只在__init__必要时调用父类方法会更清楚。

回答by Ignacio Vazquez-Abrams

__new__()can return objects of types other than the class it's bound to. __init__()only initializes an existing instance of the class.

__new__()可以返回其绑定的类以外的类型的对象。__init__()只初始化类的现有实例。

>>> class C(object):
...   def __new__(cls):
...     return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5

回答by senderle

There are probably other uses for __new__but there's one really obvious one: You can't subclass an immutable type without using __new__. So for example, say you wanted to create a subclass of tuple that can contain only integral values between 0 and size.

可能还有其他用途,__new__但有一个非常明显的用途:您不能在不使用__new__. 例如,假设您想创建一个 tuple 的子类,该子类只能包含 0 和 之间的整数值size

class ModularTuple(tuple):
    def __new__(cls, tup, size=100):
        tup = (int(x) % size for x in tup)
        return super(ModularTuple, cls).__new__(cls, tup)

You simply can't do this with __init__-- if you tried to modify selfin __init__, the interpreter would complain that you're trying to modify an immutable object.

你根本不能这样做__init__——如果你试图修改selfin __init__,解释器会抱怨你试图修改一个不可变的对象。

回答by Noufal Ibrahim

Not a complete answer but perhaps something that illustrates the difference.

不是一个完整的答案,但也许可以说明差异。

__new__will always get called when an object has to be created. There are some situations where __init__will not get called. One example is when you unpickle objects from a pickle file, they will get allocated (__new__) but not initialised (__init__).

__new__在必须创建对象时总是会被调用。在某些情况下__init__不会被调用。一个例子是当你从一个 pickle 文件中 unpickle 对象时,它们将被分配 ( __new__) 但不会被初始化 ( __init__)。

回答by lonetwin

Just want to add a word about the intent(as opposed to the behavior) of defining __new__versus __init__.

只想添加一个关于定义vs的意图(而不是行为)的词。__new____init__

I came across this question (among others) when I was trying to understand the best way to define a class factory. I realized that one of the ways in which __new__is conceptually different from __init__is the fact that the benefit of __new__is exactly what was stated in the question:

当我试图理解定义类工厂的最佳方法时,我遇到了这个问题(以及其他问题)。我意识到在__new__概念上不同于的方式之一是__init__好处__new__正是问题中所述:

So the only benefit of the __new__ method is that the instance variable will start out as an empty string, as opposed to NULL. But why is this ever useful, since if we cared about making sure our instance variables are initialized to some default value, we could have just done that in the __init__ method?

所以 __new__ 方法的唯一好处是实例变量将以空字符串开始,而不是 NULL。但是为什么这总是有用的,因为如果我们关心确保我们的实例变量被初始化为某个默认值,我们可以在 __init__ 方法中完成它?

Considering the stated scenario, we care about the initial values of the instance variables when the instanceis in reality a class itself. So, if we are dynamically creating a class object at runtime and we need to define/control something special about the subsequent instances of this class being created, we would define these conditions/properties in a __new__method of a metaclass.

考虑到所述场景,当实例实际上是一个类本身时,我们关心实例变量的初始值。因此,如果我们在运行时动态创建一个类对象,并且我们需要定义/控制有关创建的该类的后续实例的一些特殊内容,我们将在__new__元类的方法中定义这些条件/属性。

I was confused about this until I actually thought about the application of the concept rather than just the meaning of it. Here's an example that would hopefully make the difference clear:

我对此感到困惑,直到我真正考虑了这个概念的应用,而不仅仅是它的含义。这里有一个例子,希望能清楚地说明区别:

a = Shape(sides=3, base=2, height=12)
b = Shape(sides=4, length=2)
print(a.area())
print(b.area())

# I want `a` and `b` to be an instances of either of 'Square' or 'Triangle'
# depending on number of sides and also the `.area()` method to do the right
# thing. How do I do that without creating a Shape class with all the
# methods having a bunch of `if`s ? Here is one possibility

class Shape:
    def __new__(cls, sides, *args, **kwargs):
        if sides == 3:
            return Triangle(*args, **kwargs)
        else:
            return Square(*args, **kwargs)

class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return (self.base * self.height) / 2

class Square:
    def __init__(self, length):
        self.length = length

    def area(self):
        return self.length*self.length

Note this is just an demonstartive example. There are multiple ways to get a solution without resorting to a class factory approach like above and even if we do choose to implelent the solution in this manner, there are a little caveats left out for sake of brevity (for instance, declaring the metaclass explicitly)

请注意,这只是一个演示示例。有多种方法可以在不使用上述类工厂方法的情况下获得解决方案,即使我们确实选择以这种方式实现解决方案,为了简洁起见,也有一些注意事项被省略(例如,显式声明元类)

If you are creating a regular class (a.k.a a non-metaclass), then __new__doesn't really make sense unless it is special case like the mutable versus immutable scenario in ncoghlan's answeranswer (which is essentially a more specific example of the concept of defining the initial values/properties of the class/type being created via __new__to be then initialized via __init__).

如果您正在创建一个常规类(又名非元类),那么__new__除非是特殊情况,例如ncoghlan 的答案中的可变与不可变场景(这本质上是定义概念的更具体示例)通过创建的类/类型的初始值/属性__new__然后通过__init__)进行初始化。