2017-08-04 100 views
6

我明白__new__是一個靜態方法和super()可以從它被稱爲創建一個新的對象,像這樣:爲什麼super()不能使用__new__以外的靜態方法?

>>> class A: 
...  def __new__(cls): 
...   print('__new__ called') 
...   return super().__new__(cls) 
... 
>>> a = A() 
__new__ called 

爲什麼不與其他靜態方法super呼叫工作?爲什麼以下失敗?

>>> class B: 
...  @staticmethod 
...  def funcB(): 
...   print('funcB called') 
... 
>>> class C(B): 
...  @staticmethod 
...  def funcC(): 
...   print('funcC called') 
...   super().funcB() 
... 
>>> c = C() 
>>> c.funcC() 
funcC called 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in funcC 
RuntimeError: super(): no arguments 
+1

https://bugs.python.org/issue15753。 '超(C,C).funcC()'會適用於你的情況。 –

回答

3

super()在Python 3中沒有參數基本上是對其基於參數的版本的破解。

super()就沒有得到論據它提取使用名爲__class__一個特殊單元的變量即類中的第一個參數和第二個參數是會得到從棧中第一個局部變量(這將是函數的第一個參數)。

如果是__new__,它可以同時得到(__class__cls)並正常工作。

但是在這種情況下,除了__class__以外,沒有第二個變量可用,因此失敗。

class A: 
    @staticmethod 
    def func(): 
    super().func() # super(__class__, <missing>).func() 


A().func() # RuntimeError: super(): no arguments 

現在,如果我們改變它接受一個參數,那麼事情的變化:

class A: 
    @staticmethod 
    def func(foo): 
    super().func() 


# This fails because super(B, 1).func() doesn't make sense. 
A().func(1) # TypeError: super(type, obj): obj must be an instance or subtype of type 
# Works! But as there's no parent to this class with func() it fails as expected. 
A().func(A()) # AttributeError: 'super' object has no attribute 'func' 

因此,唯一的解決辦法是作出明確與super()的東西,你的情況:

super(C, C).funcC() 

一般來說,我不確定爲什麼在staticmethod的情況下執行不能出現異常,使用__class__爲這兩個參數使其工作。


相關CPython code

static int 
super_init(PyObject *self, PyObject *args, PyObject *kwds) 
{ 
    superobject *su = (superobject *)self; 
    PyTypeObject *type = NULL; 
    PyObject *obj = NULL; 
    PyTypeObject *obj_type = NULL; 

    if (!_PyArg_NoKeywords("super", kwds)) 
     return -1; 
    if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj)) 
     return -1; 

    if (type == NULL) { 
     /* Call super(), without args -- fill in from __class__ 
      and first local variable on the stack. */ 
     PyFrameObject *f; 
     PyCodeObject *co; 
     Py_ssize_t i, n; 
     f = PyThreadState_GET()->frame; 
     if (f == NULL) { 
      PyErr_SetString(PyExc_RuntimeError, 
          "super(): no current frame"); 
      return -1; 
     } 
     co = f->f_code; 
     if (co == NULL) { 
      PyErr_SetString(PyExc_RuntimeError, 
          "super(): no code object"); 
      return -1; 
     } 
     if (co->co_argcount == 0) { 
      PyErr_SetString(PyExc_RuntimeError, 
          "super(): no arguments"); 
      return -1; 
     } 
     ... 
+0

感謝您澄清,'super(C,C).funcB()'確實有效。有一件事:當我們寫'super(type,type2)'時,根據文檔'type2'應該是'type'的子類。但是這兩種類型都是相同的,即'C'。 – debashish

+1

@Deb每種類型都是它自己的一種子類型。 –

0

你是對的,但__new__是採取其中的一個實例是要求作爲第一個參數類中的靜態方法(特例,因此你不必聲明它是這樣)。它的理想行爲有點像classmethod

但是,當您調用靜態方法方法時,該方法不會收到任何內容,無法從其調用的對象或類中知道該方法。這就是爲什麼你不能在靜態方法中訪問super的原因。

因此,新的根據定義有一個類參數,它可以使用超級。

對於你的邏輯,你應該打一個電話來安裝你的對象並調用你想從那裏調用的函數。

+1

'super()'不關心誰調用函數,'super()'使用'__class__'變量來找出當前函數的類。 –

0

__new__被python解釋器視爲一種特殊情況(幾乎所有的「dunder」方法都是)。這種特殊情況處理的一個方面是使super有權訪問底層類對象,通常staticmethod沒有。您可以在the source code for the type object中搜索__new__以查看引擎蓋下正在發生的事情。

爲了解決你想要繼承的東西,你可能需要一個classmethod

>>> class B: 
...  @classmethod 
...  def funcB(cls): 
...   print('funcB called') 
... 
>>> class C(B): 
...  @classmethod 
...  def funcC(cls): 
...   print('funcC called') 
...   super().funcB() 
... 
>>> c = C() 
>>> c.funcC() 
funcC called 
funcB called 
+2

['__new__'](https://docs.python.org/3/reference/datamodel.html#object.__new__)是一種靜態方法。 –

+1

@AshwiniChaudhary我意識到他們在那裏說靜態方法,但如果這是一個文檔錯誤,我不會感到驚訝。我將深入研究這一點。它表現爲類方法,因爲它獲取傳遞給它的類對象。 – SethMMorton

+0

@AshwiniChaudhary看起來你是對的... [它在C源代碼](https://github.com/python/cpython/blob/3.6/Objects/typeobject.c#L2597)。這似乎是因爲'__new__'被解釋器特別對待。 – SethMMorton

相關問題