为什么我们不能将'self'解包到一个方法中?

9
>>> class Potato(object):
...     def method(self, spam):
...         print self, spam
... 
>>> spud = Potato()

工作内容:

>>> Potato.method(spud, **{'spam': 123})
<__main__.Potato object at 0x7f86cd4ee9d0> 123

无法工作:

>>> Potato.method(**{'self': spud, 'spam': 123})
# TypeError

但为什么不行呢?我以为'self'只是一种惯例,并没有什么本质上特别的地方?

3
这是一个有趣的问题。一个想法是:约定是self是第一个参数,而不是它被称为self。也许它并不希望通过名称展开,而是通过位置展开。 - salezica
2
你在使用 Potato.method(self=spud, spam=123) 时遇到了同样的问题。星号参数并不是真正的问题所在。 - Ismail Badawi
1
正是因为self只是一种约定,所以你不能这样做。因为self这个名称没有特殊的状态,Python只知道哪个参数是self,因为它在参数列表中是第一个,所以必须按位置传递。 - BrenBarn
@BrenBarn:从理论上讲,方法包装器对象可以检测第一个参数的名称,但是这并不值得额外的努力,因为直接调用未绑定的方法并不是常规做法。 - Martijn Pieters
这很有趣,因为当我尝试将先前实例化的马铃薯指定为方法中'self'的默认值时,我收到了一个有趣的消息:TypeError: unbound method method() must be called with Potato instance as first argument (got Potato instance instead) :) - wim
1个回答

12

Python 2的instancemethod包装对象坚持检查第一个位置参数,这个检查不支持关键字参数句号

>>> Potato.method(self=spud, spam=123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method method() must be called with Potato instance as first argument (got nothing instead)
请注意,我在那里没有使用参数解包!你可以很好地使用位置参数:
>>> Potato.method(*(spud,), **{'spam': 123})
<__main__.Potato object at 0x1002b57d0> 123

或者您可以访问原始函数对象:

>>> Potato.method.__func__(**{'self': spud, 'spam': 123})
<__main__.Potato object at 0x1002b57d0> 123

为了绕过这个限制。

Python 3不再使用未绑定方法的方法包装器;而是直接返回底层函数。


请注意第2561行的 self = PyTuple_GET_ITEM(arg, 0); ,没有尝试检查 kw 字典。 (注:此为 Python 源代码中的一段注释) - user2357112
@user2357112: 那为什么它应该这样做呢?它必须反射解包函数以查看第一个参数的名称,所有这些只是为了支持一个极端边缘情况,而这个情况可以通过直接解包方法对象来轻松解决。 - Martijn Pieters
这只是CPython实现的细节吗?如果您挖掘kwargs以查找与argspec中第一个参数的参数名称匹配的kwarg,它仍将被称为“python”吗?编辑:这个问题的答案在链接的重复项中得到了解决。 - wim
@wim:数据模型的用户定义方法部分描述了行为,即*当调用未绑定的用户定义方法对象时,将调用底层函数(im_func),但第一个参数必须是适当类(im_class)的实例或其派生类的实例,没有任何警告它是CPython实现的细节。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接