3

Can someone explain what is happening here?

class Test(object):
    __getitem__ = getattr

t = Test()
t['foo']

gives error (in Python 2.7 and 3.1):

TypeError: getattr expected at least 2 arguments, got 1

whereas:

def f(*params):
     print params    # or print(params) in 3.1

class Test(object):
    __getitem__ = f

prints the two parameters I'd expect.

James Hopkin
  • 13,797
  • 1
  • 42
  • 71

2 Answers2

6

Confusingly, built-in functions (and certain other types of callables) do not become bound methods as normal functions do when used in a class:

>>> class Foo(object): __getitem__ = getattr
>>> Foo().__getitem__
<built-in function getattr>

Compared to:

>>> def ga(*args): return getattr(*args)
>>> class Foo(object): __getitem__ = ga
>>> Foo().__getitem__
<bound method Foo.ga of <__main__.Foo object at 0xb77ad94c>>

So, getattr is not correctly receiving the first ('self') parameter. You'll need to write a normal method to wrap it.

  • Thanks. Is there a PEP or something in the docs that explains the motivation for this? – James Hopkin Sep 13 '10 at 13:49
  • Not to my knowledge; I learned about it the hard way too. Probably a combination of not wanting to bind everything with a \__call__ and a decision made a long time ago with no real reason to change it, and now it would be incompatible to change it. –  Sep 13 '10 at 13:52
0

getattr is being called without the 'self' parameter because it's assigned to an object property.

You want to do this:

__getitem__ = lambda *a, **k: getattr(*a, **k)

That will give you the output you seem to want.

Chris R
  • 17,546
  • 23
  • 105
  • 172
  • Normally assigning plain functions to class properties will create an unbound version during class creation, then a bound version during instantiation, and so work as the asker expects. (Your example relies on this feature too.) The issue is that getattr is not a plain function, but a built-in. –  Sep 13 '10 at 13:46
  • 3
    Pointless use of lambda; use a def. –  Sep 27 '10 at 17:24