What is Python\'s equivalent of Ruby\'s method_missing
method? I tried using __getattr__
but this hook applies to fields too. I only want to interc
Python doesn't distinguish between methods and attributes (a.k.a. "instance variables") the way Ruby does. Methods and other object attributes are looked up in exactly the same way in Python -- not even Python knows the difference at the look-up stage. Until the attribute is found, it's just a string.
So if you're asking for a way to ensure that __getattr__
is only called for methods, I'm afraid you probably won't find an elegant solution. But it's easy enough to simply return a function (or even a brand-new dynamically bound method) from __getattr__
.
Although I don't recommend it!!!!!!!!!!!!!!!!!!!!!
this sort of comes closer to implementing the behavior of calling the special method for every name that does not correspond to a callable attribute/method. Of course they still don't really have separate namespaces so it may feel a bit weird. It works by overriding __getattribute__
which works at a lower level then __getattr__
it tries to fetch an attribute if it fails it returns a curried special method to call with the name you called it with, if it succeeds it passes it on if its callable otherwise it
wraps the result with a proxy object which acts in almost exactly the same way afterwards except it implements call with your special method.
It doesn't allow you to access the calling object because I couldn't think of a good way to do that without sort of leaking memory(the calling object) if it's already a non-callable attribute which you store(the only think I can think of is to start a new thread that deletes it after a minute, by then you have presumably called it unless you are using it in a closure which wouldn't be supported in that case).
Edit: I forgot callable may have some false positives.
depends on the http://pypi.python.org/pypi/ProxyTypes library
from peak.util.proxies import ObjectWrapper
from functools import partial
def m(name, *args, **kwargs):
print(name,repr(args),repr(kwargs))
class CallProxy(ObjectWrapper):
def __init__(self, obj, m, method_name):
ObjectWrapper.__init__(self, obj)
object.__setattr__(self, "_method_name", method_name)
object.__setattr__(self, "_m", m)
def __call__(self, *args, **kwargs):
return self._m(self._method_name, *args,**kwargs)
class Y(object):
def __init__(self):
self.x = [3]
def __getattribute__(self, name):
try:
val = object.__getattribute__(self, name)
if not callable(val):
return CallProxy(val, m, name)
else:
return val
except AttributeError:
return partial(m, name)
In [2]: y=Y()
In [3]: y.x
Out[3]: [3]
In [4]: y.z
Out[4]: <functools.partial at 0x2667890>
In [5]: y.zz([12])
('zz', '([12],)', '{}')
In [6]: y.x.append(5)
In [7]: y.x
Out[7]: [3, 5]
In [8]: y.x(1,2,3,key="no")
('x', '(2, 3)', "{'key': 'no'}")
You could implement a missing_method like feature in the below way:
https://gist.github.com/gterzian/6400170
import unittest
from functools import partial
class MethodMissing:
def method_missing(self, name, *args, **kwargs):
'''please implement'''
raise NotImplementedError('please implement a "method_missing" method')
def __getattr__(self, name):
return partial(self.method_missing, name)
class Wrapper(object, MethodMissing):
def __init__(self, item):
self.item = item
def method_missing(self, name, *args, **kwargs):
if name in dir(self.item):
method = getattr(self.item, name)
if callable(method):
return method(*args, **kwargs)
else:
raise AttributeError(' %s has not method named "%s" ' % (self.item, name))
class Item(object):
def __init__(self, name):
self.name = name
def test(self, string):
return string + ' was passed on'
class EmptyWrapper(object, MethodMissing):
'''not implementing a missing_method'''
pass
class TestWrapper(unittest.TestCase):
def setUp(self):
self.item = Item('test')
self.wrapper = Wrapper(self.item)
self.empty_wrapper = EmptyWrapper()
def test_proxy_method_call(self):
string = self.wrapper.test('message')
self.assertEqual(string, 'message was passed on')
def test_normal_attribute_not_proxied(self, ):
with self.assertRaises(AttributeError):
self.wrapper.name
self.wrapper.name()
def test_empty_wrapper_raises_error(self, ):
with self.assertRaises(NotImplementedError):
self.empty_wrapper.test('message')
if __name__ == '__main__':
unittest.main()
There is no difference in Python between properties and methods. A method is just a property, whose type is just instancemethod
, that happens to be callable (supports __call__
).
If you want to implement this, your __getattr__
method should return a function (a lambda
or a regular def
, whatever suite your needs) and maybe check something after the call.