Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I pass self as a named argument to an instance method in Python?

This works:

>>> def bar(x, y):
...     print x, y
...
>>> bar(y=3, x=1)
1 3

And this works:

>>> class Foo(object):
...     def bar(self, x, y):
...             print x, y
...
>>> z = Foo()
>>> z.bar(y=3, x=1)
1 3

And even this works:

>>> Foo.bar(z, y=3, x=1)
1 3

But why doesn't this work in Python 2.x?

>>> 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)

This makes metaprogramming more difficult, because it requires special case handling. I'm curious if it's somehow necessary by Python's semantics or just an artifact of implementation.

like image 309
Joseph Garvin Avatar asked Mar 14 '10 20:03

Joseph Garvin


1 Answers

z.bar is a bound method -- it already has an im_self attribute that becomes the first argument (conventionally named self) to the underlying function object, the bound method's im_func attribute. To override that you obviously need to re-bind im_self (edit: or call the im_func instead) -- whatever you do in terms of argument passing is not going to have any effect on it, of course. Yep, that's the documented way bound methods object work in Python (not just an implementation detail: every correct Python implementation has to do it exactly this way). So it's "necessary" in the sense that it's part of what makes Python exactly the language it is, as opposed to being a slighty or strongly different language. Of course you could design a different language that chooses to play by completely different rules, but -- it wouldn't be Python, of course.

Edit: the OP's edits clarified he's calling an unbound method, not a bound one. This still doesn't work, and the reason is clear from the error message the attempt gets:

TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)

The rule underlying this very clear error message is that the instance must be the first argument (so of course a positional one: named arguments have no ordering). The unbound method doesn't "know" (nor care) what that parameter's name may be (and the use of name self for it is only a convention, not a rule of the Python language): it only care about the unambiguous condition of "first argument" (among the positional ones, of course).

This obscure corner case could certainly be altered (with a Python 3.2 patch, if and when the language-changes "freeze" ends;-) by making unbound methods seriously more complicated: they'd have to introspect and save the first-argument's name at creation time, and check keyword arguments on each call just in case somebody's passing self by name instead of by position. I don't think this would break any existing, working code, it would only slow down just about every existing Python program. If you write and propose a patch implementing this complication, and get active on python-dev to advocate for it against the sure-to-come firestorm of opposition, you do no doubt stand a > 0 chance to ram it through -- good luck.

The rest of us, meanwhile, will keep getting the im_func attribute instead, as one absurdly-tiny extra step in what has to be a pretty complicated inded edifice of metaprogramming to warrant such a change -- it isn't a "special case" at all, compared with the horrid difficulties of adapting named-argument passing to builtins that don't take named arguments (and don't expose their "argument names" to easily allow the transformation of named arguments into positional ones (now that would be a windmill worth attacking, IMHO: of all callables, builtins are the worst to metaprogram about, because of that!-).

like image 150
Alex Martelli Avatar answered Sep 28 '22 03:09

Alex Martelli