什么是Python相当于Ruby的method_missing
方法?我尝试使用__getattr__
但这个钩子也适用于字段。我只想拦截方法调用。 Python的方法是什么?
答案 0 :(得分:27)
Python在属性和方法之间没有区别。方法只是一个属性,其类型只是instancemethod
,恰好是可调用的(支持__call__
)。
如果你想实现这一点,你的__getattr__
方法应该返回一个函数(lambda
或常规def
,无论你需要什么套件),也可以在通话后检查一下。
答案 1 :(得分:4)
Python不像Ruby那样区分方法和属性(a.k.a。“实例变量”)。在Python中以完全相同的方式查找方法和其他对象属性 - 甚至Python都不知道查找阶段的差异。在找到属性之前,它只是一个字符串。
因此,如果你想要一种方法来确保__getattr__
仅 调用方法,我恐怕你可能找不到一个优雅的解决方案。但是从__getattr__
简单地返回一个函数(甚至是一个全新的dynamically bound method)就很容易了。
答案 2 :(得分: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()
答案 3 :(得分:1)
虽然我不推荐它!!!!!!!!!!!!!!!!!!!!!
这种更接近实现为每个与可调用属性/方法不对应的名称调用特殊方法的行为。当然,他们仍然没有单独的命名空间,因此可能会感觉有点奇怪。它通过重写__getattribute__
工作在较低级别然后__getattr__
它尝试获取属性如果失败它返回一个curried特殊方法用你调用它的名称调用,如果它成功的话如果它可以调用则传递它,否则它
用一个代理对象包装结果,后面的代理方式几乎完全相同,除非它用你的特殊方法实现调用。
它不允许你访问调用对象,因为如果它已经是你存储的不可调用属性(如果它已经是一个不可调用的属性),那么如果没有泄漏内存(调用对象),我就无法想到一个好的方法。只是认为我能想到的是在一分钟之后启动一个删除它的新线程,到那时你可能会调用它,除非你在一个在这种情况下不支持的闭包中使用它。)
编辑:我忘记了可赎回可能有一些误报。
取决于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'}")