Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining __getattr__ and __getitem__ on a function has no effect

Disclaimer This is just an exercise in meta-programming, it has no practical purpose.

I've assigned __getitem__ and __getattr__ methods on a function object, but there is no effect...

def foo():
  print "foo!"

foo.__getitem__ = lambda name: name
foo.__getattr__ = lambda name: name
foo.baz = 'baz'

Sanity check that we can assign properties to a function:

>>> foo.baz
'baz'

Neat. How about the "magic getters"?

>>> foo.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'

>>> foo['foo']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'function' object is not subscriptable

>>> getattr(foo, 'bar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'

Is it possible to have a "magic getter" on a function object?

like image 931
colinta Avatar asked May 29 '12 16:05

colinta


2 Answers

Nope! Assigning __getitem__ to an instance doesn't work on any type of object:

>>> class A(object):
...   pass
...
>>> a = A()
>>> a.__getattr__ = lambda name: name
>>> a.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'foo'

And you can't define __getattr__ on the built-in function type:

>>> import types
>>> types.FunctionType.__getitem__ = lambda name: name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'function'

And you can't subclass types.FunctionType:

>>> import types
>>> class F(types.FunctionType):
...   pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
type 'function' is not an acceptable base type
like image 139
colinta Avatar answered Nov 08 '22 03:11

colinta


At least on new-style classes (which are the only kind in Python 3 and the kind you should be using in Python 2), Python only looks for magic methods on the class (and its ancestors), never on the instance. Docs here.

And of course you can't modify the function type, or derive from it. As you've found, however, any class with a __call__() method makes callable instances, so that's the way to do it.

like image 28
kindall Avatar answered Nov 08 '22 02:11

kindall