为什么 super() 不能与 __new__ 以外的静态方法一起使用?
Why doesn't super() work with static methods other than __new__?
我知道 __new__
是一个静态方法,可以从中调用 super()
创建一个新对象,如下所示:
>>> 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
你是对的,但是 __new__
是一个静态方法(特殊情况,所以你不需要这样声明)它将请求实例的 class 作为它的第一个参数.它的理想行为有点像 class 方法 .
但是当您调用 staticmethod 方法时,该方法不会收到任何东西,也无法知道它是从哪个对象或 class 调用的。这就是为什么你不能在 staticmethod.
中访问 super 的原因
因此,根据定义,new 中有一个 class 参数,并且可以与它一起使用 super。
对于您的逻辑,您应该调用以实例化您的对象并从那里调用您想要调用的函数。
__new__
被 python 解释器视为特例(几乎所有 "dunder" 方法都是)。这种特殊情况处理的一个方面是使 super
可以访问底层的 class 对象,而通常 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
super()
在 Python 3 中没有参数基本上是对其基于参数的版本的破解。
当 super()
没有参数时,它会使用名为 __class__
的特殊单元格变量获取第一个参数,即 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()
总的来说,我不确定为什么在静态方法的情况下的实现不能产生异常并使用 __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;
}
...
解决方案是使用 "classmethod" 而不是 "staticmethod" 例如
class A:
@staticmethod
def a():
return "in a"
@classmethod
def foo(cls):
print(cls.a())
class B(A):
@staticmethod
def a():
return "in b"
B.foo()
## in b
我知道 __new__
是一个静态方法,可以从中调用 super()
创建一个新对象,如下所示:
>>> 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
你是对的,但是 __new__
是一个静态方法(特殊情况,所以你不需要这样声明)它将请求实例的 class 作为它的第一个参数.它的理想行为有点像 class 方法 .
但是当您调用 staticmethod 方法时,该方法不会收到任何东西,也无法知道它是从哪个对象或 class 调用的。这就是为什么你不能在 staticmethod.
中访问 super 的原因因此,根据定义,new 中有一个 class 参数,并且可以与它一起使用 super。
对于您的逻辑,您应该调用以实例化您的对象并从那里调用您想要调用的函数。
__new__
被 python 解释器视为特例(几乎所有 "dunder" 方法都是)。这种特殊情况处理的一个方面是使 super
可以访问底层的 class 对象,而通常 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
super()
在 Python 3 中没有参数基本上是对其基于参数的版本的破解。
当 super()
没有参数时,它会使用名为 __class__
的特殊单元格变量获取第一个参数,即 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()
总的来说,我不确定为什么在静态方法的情况下的实现不能产生异常并使用 __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;
}
...
解决方案是使用 "classmethod" 而不是 "staticmethod" 例如
class A:
@staticmethod
def a():
return "in a"
@classmethod
def foo(cls):
print(cls.a())
class B(A):
@staticmethod
def a():
return "in b"
B.foo()
## in b