创建元组的子类时调用 __new__

Calling __new__ when making a subclass of tuple

在Python中,当subclassing元组时,__new__函数以self作为参数被调用。例如,这里是 PySpark 的 Row class:

的释义版本
class Row(tuple):
    def __new__(self, args):
        return tuple.__new__(self, args)

但是 help(tuple) 没有向 __new__ 显示 self 参数:

  __new__(*args, **kwargs) from builtins.type
      Create and return a new object.  See help(type) for accurate signature.

help(type)说的是同一件事:

__new__(*args, **kwargs)
      Create and return a new object.  See help(type) for accurate signature.

那么 self 如何传递给 Row class 定义中的 __new__

是否可以查看 tuple.__new__ 的来源以便我自己查看答案?

我的问题不是 this one 的重复问题,因为在那个问题中,所有讨论都提到 __new__ 方法,这些方法明确地将 selfcls 作为第一个参数。我正在努力理解

  1. 为什么 tuple.__new__ 方法没有 selfcls 作为第一个参数。
  2. 我如何着手检查元组的源代码 class,亲眼看看到底发生了什么。

tuple.__new__

的正确签名

用 C 实现的函数和类型通常无法检查,而且它们的签名看起来总是那样。

tuple.__new__的正确签名是:

__new__(cls[, sequence])

例如:

>>> tuple.__new__(tuple)
()
>>> tuple.__new__(tuple, [1, 2, 3])
(1, 2, 3)

毫不奇怪,这与调用 tuple() 完全一样,只是您必须重复 tuple 两次。


__new__

的第一个参数

请注意 __new__ 的第一个参数始终是 class,而不是实例。其实__new__的作用就是创建和return新的实例。

特殊方法__new__是一个静态方法。

我这么说是因为在你的 Row.__new__ 中我可以看到 self:虽然参数的名称并不重要(使用关键字参数时除外),但请注意 self 将是 RowRow 的子 class,而不是实例。一般约定是将第一个参数命名为 cls 而不是 self.


回到你的问题

So how does self get passed to __new__ in the Row class definition?

当您调用 Row(...) 时,Python 会自动调用 Row.__new__(Row, ...)

  • Is it via *args?

你可以这样写你的Row.__new__

class Row(tuple):
    def __new__(*args, **kwargs):
        return tuple.__new__(*args, **kwargs)

这行得通,没有任何问题。如果您不关心参数,这将非常有用。

  • Does __new__ have some subtlety where its signature can change with context?

不,__new__唯一特别的地方在于它是一个静态方法。

  • Or, is the documentation mistaken?

我会说它不完整或模棱两可。

  • Why the tuple.__new__ method does not have self or cls as first argument.

确实有,只是没有出现在 help(tuple.__new__) 上,因为 C 中实现的函数和方法通常不会公开这些信息。

  • How I might go about examining the source code of the tuple class, to see for myself what's really going on.

您要查找的文件是Objects/tupleobject.c. Specifically, you are interested in the tuple_new()函数:

static char *kwlist[] = {"sequence", 0};
/* ... */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:tuple", kwlist, &arg))

这里的"|O:tuple"表示:函数调用"tuple",它接受一个可选参数(|分隔可选参数,O代表一个Python 目的)。可选参数可以通过关键字参数 sequence.

设置

关于help(type)

作为参考,您正在查看 type.__new__ 的文档,而您应该停在 help(type) 的前四行:

__new__() 的情况下,正确的签名是 type() 的签名:

class type(object)
 |  type(object_or_name, bases, dict)
 |  type(object) -> the object's type
 |  type(name, bases, dict) -> a new type

但这无关紧要,因为 tuple.__new__ 有不同的签名。


记住super()

最后但同样重要的是,尝试使用 super() 而不是直接调用 tuple.__new__()