Python equivalent of Ruby's 'method_missing'

后端 未结 4 1093
旧巷少年郎
旧巷少年郎 2020-12-29 04:10

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

相关标签:
4条回答
  • 2020-12-29 04:41

    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__.

    0 讨论(0)
  • 2020-12-29 04:46

    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'}")
    
    0 讨论(0)
  • 2020-12-29 04:47

    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()
    
    0 讨论(0)
  • 2020-12-29 04:48

    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.

    0 讨论(0)
提交回复
热议问题