【问题标题】:Python equivalent of Ruby's 'method_missing'Python 相当于 Ruby 的“method_missing”
【发布时间】:2011-10-05 22:59:38
【问题描述】:

Python 与 Ruby 的 method_missing 方法等效的是什么?我尝试使用__getattr__,但这个钩子也适用于字段。我只想拦截方法调用。 Python 的实现方式是什么?

【问题讨论】:

    标签: python ruby oop metaprogramming method-missing


    【解决方案1】:

    在 Python 中,属性和方法没有区别。方法只是一个属性,它的类型只是instancemethod,恰好是可调用的(支持__call__)。

    如果您想实现这一点,您的 __getattr__ 方法应该返回一个函数(lambda 或常规 def,无论您需要什么套件),并可能在调用后检查一些内容。

    【讨论】:

    • 谢谢。我通过谷歌搜索找到了this
    • 作为参考,有问题的链接通过定义应该被视为方法的属性名称列表来解决歧义(这听起来有点违背了目的,因为您可以 define 每个方法并委托给一个存根)。
    【解决方案2】:

    Python 不像 Ruby 那样区分方法和属性(也称为“实例变量”)。在 Python 中查找方法和其他对象属性的方式完全相同——甚至 Python 在查找阶段也不知道其中的区别。在找到属性之前,它只是一个字符串。

    因此,如果您要寻求一种方法来确保 调用 __getattr__ 方法,恐怕您可能找不到优雅的解决方案。但是从__getattr__返回一个函数(甚至是一个全新的dynamically bound method)很容易。

    【讨论】:

    • 出于同样的原因,您将在 Ruby 中使用 method_missing 或在 Smalltalk 中使用 doesNotUnderstand
    • 我理解您为什么要使用__getattr__。我只是不明白你为什么“只想拦截方法调用”。
    • Ruby 根本不区分方法和属性——在 Ruby 中没有属性之类的东西。
    • @steenslag,这对我来说听起来像是一个非常奇怪的说法。当我说“属性”时,我的意思是“内部状态”。您是否声称 Ruby 中的对象没有内部状态?或者您的意思是 Ruby 中对象的内部状态始终是私有的?确实如此。我想在 Ruby 语言中,属性实际上是“实例变量”的访问器方法。但是由于我们在谈论 Python,所以我使用的是 Python 语言。
    • 在 Ruby 术语中,Python 的 __getattr__ 介于 method_missing 和覆盖 Hash#[] 之间,为丢失的键做一些特殊的事情。
    【解决方案3】:

    您可以通过以下方式实现类似 missing_method 的功能:

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

    【讨论】:

      【解决方案4】:

      虽然我不推荐!!!!!!!!!!!!!!!!!!!!!

      这种方式更接近于实现为每个不对应于可调用属性/方法的名称调用特殊方法的行为。当然,它们仍然没有真正的独立命名空间,所以可能感觉有点奇怪。它通过覆盖__getattribute__ 来工作,它在较低级别上工作然后__getattr__ 它尝试获取一个属性如果它失败它返回一个咖喱​​特殊方法来调用你调用它的名称,如果它成功它传递它如果它是可调用的,否则它 使用代理对象包装结果,该代理对象的行为方式几乎完全相同,只是它使用您的特殊方法实现调用。

      它不允许您访问调用对象,因为如果它已经是您存储的不可调用属性(唯一认为我能想到的是启动一个新线程,在一分钟后将其删除,到那时您可能已经调用了它,除非您在这种情况下不支持的闭包中使用它)。

      编辑:我忘记了 callable 可能有一些误报。

      取决于http://pypi.python.org/pypi/ProxyTypes

      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'}")
      

      【讨论】:

        猜你喜欢
        • 2020-02-16
        • 2019-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-27
        相关资源
        最近更新 更多