[英]Instance of Python class that responds to all method calls
有没有办法创建一个实例响应任意方法调用的类?
我知道有一个特殊的方法__getattr__(self, attr)
当有人试图访问实例的属性时会调用它。 我也在寻找类似的东西,让我也能拦截方法调用。 所需的行为看起来像这样:
class A(object):
def __methodintercept__(self, method, *args, **kwargs): # is there a special method like this??
print(str(method))
>>> a = A()
>>> a.foomatic()
foomatic
编辑
其他建议的问题没有解决我的情况:我不想包装另一个类或更改第二个类或类似类的元类。 我只想有一个响应任意方法调用的类。
感谢 jonrshape,我现在知道__getattr__(self, attr)
也将在调用方法时调用,就像访问属性时一样。 但是我如何在__getattr__
区分attr
是来自方法调用还是属性访问,以及如何获取潜在方法调用的参数?
这是我想出来的,它的行为就像该方法存在一样。
首先让我们确定一件事:您无法在__getattr__
区分attr
是来自函数调用还是“属性访问”,因为类方法是您的类的属性。 因此,即使有人不打算调用该方法,也可以访问该方法,例如:
class Test:
def method(self):
print "Hi, I am method"
>> t = Test()
>> t.method # just access the method "as an attribute"
<bound method Test.method of <__main__.Test instance at 0x10a970c68>>
>> t.method() # actually call the method
Hi, I am method
因此,我能想到的最接近的事情是这种行为:
创建一个类 A,这样:
我将首先编写类定义,然后展示访问不存在的方法的行为与访问存在的方法完全一样,无论您是访问它还是实际调用它。
类定义:
class A(object):
def __init__(self):
self.x = 1 # set some attribute
def __getattr__(self,attr):
try:
return super(A, self).__getattr__(attr)
except AttributeError:
return self.__get_global_handler(attr)
def __get_global_handler(self, name):
# Do anything that you need to do before simulating the method call
handler = self.__global_handler
handler.im_func.func_name = name # Change the method's name
return handler
def __global_handler(self, *args, **kwargs):
# Do something with these arguments
print "I am an imaginary method with name %s" % self.__global_handler.im_func.func_name
print "My arguments are: " + str(args)
print "My keyword arguments are: " + str(kwargs)
def real_method(self, *args, **kwargs):
print "I am a method that you actually defined"
print "My name is %s" % self.real_method.im_func.func_name
print "My arguments are: " + str(args)
print "My keyword arguments are: " + str(kwargs)
我添加了方法real_method
只是为了让我在类中实际存在一些东西,以将其行为与“虚构方法”的行为进行比较
结果如下:
>> a = A()
>> # First let's try simple access (no method call)
>> a.real_method # The method that is actually defined in the class
<bound method A.real_method of <test.A object at 0x10a9784d0>>
>> a.imaginary_method # Some method that is not defined
<bound method A.imaginary_method of <test.A object at 0x10a9784d0>>
>> # Now let's try to call each of these methods
>> a.real_method(1, 2, x=3, y=4)
I am a method that you actually defined
My name is real_method
My arguments are: (1, 2)
My keyword arguments are: {'y': 4, 'x': 3}
>> a.imaginary_method(1, 2, x=3, y=4)
I am an imaginary method with name imaginary_method
My arguments are: (1, 2)
My keyword arguments are: {'y': 4, 'x': 3}
>> # Now let's try to access the x attribute, just to make sure that 'regular' attribute access works fine as well
>> a.x
1
unittest.mock.Mock
默认执行此操作。
from unittest.mock import Mock
a = Mock()
a.arbitrary_method() # No error
a.arbitrary_method.called # True
a.new_method
a.new_method.called # False
a.new_method("some", "args")
a.new_method.called # True
a.new_method.assert_called_with("some", "args") # No error
a.new_method_assert_called_with("other", "args") # AssertionError
方法调用与属性访问没有任何不同。 __getattr__()
或__getattribute__()
是响应任意属性请求的方式。
您无法知道访问是来自“只是检索”还是“方法调用”。
它是这样工作的:首先,属性检索,然后调用检索到的对象(在 Python 中,call 只是另一个操作符:任何东西都可以被调用,如果不可调用则抛出异常)。 一个不知道,也不应该知道另一个(好吧,您可以分析调用堆栈上的代码,但这完全不是这里要做的事情)。
原因之一是 - 函数是 Python 中的一流对象,即函数(或者更确切地说,对它的引用)与任何其他数据类型没有区别:我可以获取引用、保存它并传递它。 即请求数据字段和方法之间完全没有区别。
详细说明您需要什么,以便我们提出更好的解决方案。
例如,如果您需要能够使用不同签名调用的“方法”,则可以使用*args
和**kwargs
。
这是我遇到这个问题时正在寻找的解决方案:
class Wrapper:
def __init__(self):
self._inner = [] # or whatever type you want to wrap
def foo(self, x):
print(x)
def __getattr__(self, attr):
if attr in self.__class__.__dict__:
return getattr(self, attr)
else:
return getattr(self._inner, attr)
t = Test()
t.foo('abc') # prints 'abc'
t.append('x') # appends 'x' to t._inner
非常欢迎批评。 我想向 Splinter 包中的 Browser 类添加方法,但它只公开了一个返回实例的函数,而不是类本身。 这种方法允许伪继承,这意味着我可以声明性地将 DOM 代码与特定于网站的代码分离。 (事后看来,更好的方法可能是直接使用 Selenium。)
以下将响应所有未定义的方法调用:
class Mock:
def __init__(self, *args, **kwargs):
pass
def __getattr__(self, attr):
def func(*args, **kwargs):
pass
return func
或者只是使用unittest.mock.Mock
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.