【问题标题】:Why doesn't super() work with static methods other than __new__?为什么 super() 不能与 __new__ 以外的静态方法一起使用?
【发布时间】:2018-01-11 21:54:10
【问题描述】:

我知道__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

【问题讨论】:

标签: python python-3.x static-methods super


【解决方案1】:

您是对的,但__new__ 是一个静态方法(特殊情况,因此您无需声明它),它将请求实例的类作为其第一个参数。它的理想行为有点像classmethod

但是当你调用 staticmethod 方法时,该方法不会收到任何东西,无法知道它是从哪个对象或类调用的。这就是您无法在 staticmethod 中访问 super 的原因。

因此,根据定义,new 中包含一个类参数,并且可以与它一起使用 super。

对于您的逻辑,您应该调用以实例化您的对象并从那里调用您想要调用的函数。

【讨论】:

  • super() 不关心谁调用了函数,super() 使用__class__ 变量来确定当前函数的类。
【解决方案2】:

__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

【讨论】:

  • __new__ 是一个静态方法。
  • @AshwiniChaudhary 我意识到他们在那里说静态方法,但如果这是文档错误,我不会感到惊讶。我要深入研究。它表现为类方法,因为它获取传递给它的类对象。
  • @AshwiniChaudhary 看起来你是对的...it's in the C source code。看来是因为__new__被解释器特殊对待了。
  • @AshwiniChaudhary 我更喜欢阅读源代码以了解他们在做什么,以便了解原因,而不是结果。
  • 我不记得阻止过你。 :-|
【解决方案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;
        }
        ...

【讨论】:

  • 感谢您的澄清,super(C, C).funcB() 确实有效。一件事:当我们编写super(type, type2) 时,根据文档type2 应该是type 的子类。但是这里两种类型都是相同的,即C
  • @Deb 每个类型都是它自己的子类型。
【解决方案4】:

例如,解决方案是使用“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

【讨论】:

    猜你喜欢
    • 2011-03-02
    • 2021-08-06
    • 2017-10-17
    • 2014-01-03
    • 1970-01-01
    • 1970-01-01
    • 2011-09-15
    • 2014-11-26
    • 2016-12-23
    相关资源
    最近更新 更多