为什么我不能将self作为命名参数传递给Python中的实例方法?

Jos*_*vin 5 python methods metaprogramming python-2.x language-lawyer

这有效:

>>> def bar(x, y):
...     print x, y
...
>>> bar(y=3, x=1)
1 3
Run Code Online (Sandbox Code Playgroud)

这有效:

>>> class Foo(object):
...     def bar(self, x, y):
...             print x, y
...
>>> z = Foo()
>>> z.bar(y=3, x=1)
1 3
Run Code Online (Sandbox Code Playgroud)

甚至这个工作:

>>> Foo.bar(z, y=3, x=1)
1 3
Run Code Online (Sandbox Code Playgroud)

但为什么这不起作用?

>>> Foo.bar(self=z, y=3, x=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
Run Code Online (Sandbox Code Playgroud)

这使得元编程更加困难,因为它需要特殊的案例处理.我很好奇Python的语义是否是必要的,或者仅仅是实现的工件.

Ale*_*lli 6

z.bar是一个绑定方法 - 它已经有一个im_self属性成为self底层函数对象的第一个参数(通常命名),即绑定方法的im_func属性.要覆盖它,你显然需要重新绑定im_self(编辑:或者调用它im_func) - 当然,无论你在参数传递方面做什么都不会对它产生任何影响.是的,这是文档化的方式绑定方法对象在Python的工作(不只是实现的细节:每一个正确的Python实现必须做它到底这种方式).所以它是"必要的",因为它是使Python成为语言的一部分,而不是一种轻微或强烈不同的语言.当然,你可以设计一种不同的语言,选择完全不同的规则,但是 - 当然不是Python.

编辑:OP的编辑澄清了他正在调用一个未绑定的方法,而不是绑定方法.这仍然不起作用,原因从尝试获得的错误消息中清楚:

TypeError:必须使用Foo实例作为第一个参数调用未绑定的方法bar()(没有取而代之)

这个非常明确的错误消息的基础规则是实例必须是第一个参数(所以当然是位置参数:命名参数没有排序).未绑定的方法不"知道"(也不关心)该参数的名称是什么(并且名称的使用self只是一种约定,而不是 Python语言的规则):它只关心"明确的条件"第一个论点"(当然是位置的).

这个不起眼的角落案例当然可以改变(使用Python 3.2补丁,如果语言变化"冻结"结束;-)使非绑定方法更加复杂:他们必须反省并保存第一个参数的名称在创建时,并在每次调用时检查关键字参数,以防有人self通过名称而不是按位置传递.我不认为这会破坏任何现有的工作代码,它只会减慢几乎所有现有的Python程序.如果你编写并提出一个实现这个复杂功能的补丁,并在python-dev上积极主动反对肯定会遭到反对的风暴,那么毫无疑问你有机会通过它来胜过它 - 祝你好运.

与此同时,我们其余的人将继续获得这个im_func属性,作为一个非常微小的额外步骤,必须是一个非常复杂的元编程大纲来保证这种变化 - 这不是一个"特例"所有这些都与将命名参数传递给不使用命名参数的内置函数的可怕困难相比(并且不要公开它们的"参数名称"以便容易地将命名参数转换为位置参数(现在将是一个风车值得攻击,恕我直言:在所有的callables中,builtins是关于元节目最差的,因为那个! - ).

  • @Alex:我不确定我是否同意您的说法,即“它只会减慢几乎所有现有的Python程序的速度” –在Objects / classobject.c中查看instancemethod_call(),对我来说,您可以做出必要的决定更改对主代码路径没有任何影响。话虽如此,我仍然同意你的看法,这可能不值得。:) (2认同)
  • 请注意,Python 3.2*(或任何后续的Python 3版本)没有任何补丁,因为未绑定的方法已经成为Python 3中Dodo的(应当的)方式.不再有未绑定的方法,而是解开的原始函数对象返回并且您可以将"self"参数的关键字参数用于您的内容. (2认同)