【发布时间】:2011-10-05 22:59:38
【问题描述】:
Python 与 Ruby 的 method_missing 方法等效的是什么?我尝试使用__getattr__,但这个钩子也适用于字段。我只想拦截方法调用。 Python 的实现方式是什么?
【问题讨论】:
标签: python ruby oop metaprogramming method-missing
Python 与 Ruby 的 method_missing 方法等效的是什么?我尝试使用__getattr__,但这个钩子也适用于字段。我只想拦截方法调用。 Python 的实现方式是什么?
【问题讨论】:
标签: python ruby oop metaprogramming method-missing
在 Python 中,属性和方法没有区别。方法只是一个属性,它的类型只是instancemethod,恰好是可调用的(支持__call__)。
如果您想实现这一点,您的 __getattr__ 方法应该返回一个函数(lambda 或常规 def,无论您需要什么套件),并可能在调用后检查一些内容。
【讨论】:
Python 不像 Ruby 那样区分方法和属性(也称为“实例变量”)。在 Python 中查找方法和其他对象属性的方式完全相同——甚至 Python 在查找阶段也不知道其中的区别。在找到属性之前,它只是一个字符串。
因此,如果您要寻求一种方法来确保 仅 调用 __getattr__ 方法,恐怕您可能找不到优雅的解决方案。但是从__getattr__返回一个函数(甚至是一个全新的dynamically bound method)很容易。
【讨论】:
method_missing 或在 Smalltalk 中使用 doesNotUnderstand。
__getattr__。我只是不明白你为什么“只想拦截方法调用”。
__getattr__ 介于 method_missing 和覆盖 Hash#[] 之间,为丢失的键做一些特殊的事情。
您可以通过以下方式实现类似 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()
【讨论】:
虽然我不推荐!!!!!!!!!!!!!!!!!!!!!
这种方式更接近于实现为每个不对应于可调用属性/方法的名称调用特殊方法的行为。当然,它们仍然没有真正的独立命名空间,所以可能感觉有点奇怪。它通过覆盖__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'}")
【讨论】: