簡體   English   中英

Python(和 Python C API):__new__ 與 __init__

[英]Python (and Python C API): __new__ versus __init__

我要問的問題似乎是Python 使用 __new__ 和 __init__的重復 ,但無論如何,我仍然不清楚__new____init__之間的實際區別是什么。

在你急於告訴我__new__用於創建對象而__init__用於初始化對象之前,讓我說清楚:我明白了。 事實上,這種區別對我來說很自然,因為我在 C++ 中有經驗,我們有放置 new ,它類似地將對象分配與初始化分開。

Python C API 教程是這樣解釋的:

新成員負責創建(而不是初始化)該類型的對象。 它在 Python 中作為__new__()方法__new__() ...實現新方法的一個原因是確保實例變量的初始值

所以,是的-我得到__new__呢,但盡管這樣,我還是不明白為什么它在Python是非常有用的。 給出的例子說__new__如果你想“確保實例變量的初始值”可能很有用。 那么,這不正是__init__會做的嗎?

在 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;
    }

    .....
}

請注意,如果沒有在__new__定義__new__方法,我們必須使用PyType_GenericNew ,它只是將所有實例變量成員初始化為 NULL。 所以__new__方法的唯一好處是實例變量將以空字符串開始,而不是 NULL。 但是為什么這總是有用的,因為如果我們關心確保我們的實例變量被初始化為某個默認值,我們可以在__init__方法中完成它?

差異主要出現在可變和不可變類型上。

__new__接受一個類型作為第一個參數,並且(通常)返回該類型的一個新實例。 因此它適用於可變和不可變類型。

__init__接受一個實例作為第一個參數並修改該實例的屬性。 這對於不可變類型是不合適的,因為它允許在創建后通過調用obj.__init__(*args)來修改它們。

比較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]

至於為什么它們是分開的(除了簡單的歷史原因): __new__方法需要一堆樣板才能正確(初始對象創建,然后記住最后返回對象)。 相比之下, __init__方法非常簡單,因為您只需設置您需要設置的任何屬性。

除了__init__方法更容易編寫,以及上面提到的可變與不可變的區別,通過在__new__設置任何絕對需要的實例不變量,還可以利用這種分離來使在子類中調用父類__init__成為可選。 不過,這通常是一種可疑的做法 - 通常只在必要時調用父類__init__方法會更清楚。

__new__可能還有其他用途,但有一個非常明顯的用途:您不能在不使用__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)

你根本不能用__init__做到這一點——如果你試圖在__init__修改self ,解釋器會抱怨你試圖修改一個不可變的對象。

__new__()可以返回與其綁定的類不同的類型的對象。 __init__()僅初始化類的現有實例。

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

不是一個完整的答案,但也許可以說明差異。

__new__將始終在必須創建對象時被調用。 在某些情況下__init__不會被調用。 一個例子是當你從一個 pickle 文件中 unpickle 對象時,它們將被分配( __new__ )但不會被初始化( __init__ )。

只想添加一個關於定義__new____init__意圖(而不是行為)的__new__

當我試圖理解定義類工廠的最佳方法時,我遇到了這個問題(以及其他問題)。 我意識到,在其中的方式之一__new__是從概念上不同的__init__是一個事實,即受益__new__正是在這個問題指出:

所以 __new__ 方法的唯一好處是實例變量將以空字符串開始,而不是 NULL。 但是為什么這總是有用的,因為如果我們關心確保我們的實例變量被初始化為某個默認值,我們可以在 __init__ 方法中完成它?

考慮到所述場景,當實例實際上是一個類本身時,我們關心實例變量的初始值。 因此,如果我們在運行時動態創建一個類對象,並且我們需要定義/控制有關創建的此類的后續實例的一些特殊內容,我們將在元類的__new__方法中定義這些條件/屬性。

我對此感到困惑,直到我真正考慮了這個概念的應用,而不僅僅是它的含義。 這里有一個例子,希望能清楚地說明區別:

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

請注意,這只是一個演示示例。 有多種方法可以在不使用上述類工廠方法的情況下獲得解決方案,即使我們確實選擇以這種方式實現解決方案,為了簡潔起見,也有一些注意事項被省略(例如,顯式聲明元類)

如果您正在創建一個常規類(又名非元類),那么__new__並沒有真正意義,除非它是特殊情況,例如ncoghlan 的答案答案中的可變與不可變場景(這本質上是概念的更具體示例)定義通過__new__創建的類/類型的初始值/屬性,然后通過__init__進行初始化)。

__new__一個特殊用途是使類成為單例:

class SingletonClass(object):
  def __new__(cls):
    if not hasattr(cls, 'instance'):
      cls.instance = super(SingletonClass, cls).__new__(cls)
    return cls.instance 

(來源: Python 中的單例模式 - 完整指南 - GeeksforGeeks

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM