响应所有方法调用的Python类实例

时间:2015-10-14 12:32:52

标签: python

有没有办法创建一个实例响应任意方法调用的类?

我知道当有人试图访问实例的属性时会有一个特殊的方法__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__ if attr来自方法调用或属性访问以及如何获取潜在方法调用的参数?

5 个答案:

答案 0 :(得分:8)

这是我提出的,其行为与该方法的存在完全相同。

首先让我们建立一件事:如果__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类,以便:

  1. 当我们尝试访问该类中已存在的属性/方法时,请执行normal并返回所请求的属性/方法。
  2. 当我们尝试访问类定义中不存在的内容时,将其视为类方法,并为所有此类方法提供1个全局处理程序。
  3. 我将首先编写类定义,然后展示访问不存在的方法的行为与访问存在的方法完全相同,无论您是访问它还是实际调用它。

    班级定义:

    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
    

答案 1 :(得分:5)

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

答案 2 :(得分:0)

方法调用与属性访问没有任何区别。 __getattr__()__getattribute__()是响应任意属性请求的方式。

您无法知道访问是否来自&#34;只是检索&#34;或&#34;方法调用&#34;。

它的工作原理如下:首先,属性检索然后调用检索到的对象(在Python中,调用只是另一个操作符:任何东西都可以被调用,如果它不可调用则抛出异常)。一个人没有,也不应该知道另一个(好吧,你可以分析调用堆栈中的代码,但这完全不是这里要做的事情)。

其中一个原因是 - 函数是Python中的第一类对象,即函数(或者更确切地说,对它的引用)与任何其他数据类型没有区别:我可以获取引用,保存并传递它周围。即在请求数据字段和方法之间完全没有区别。

详细说明您需要的内容,以便我们建议更好的解决方案。

,例如,如果您需要&#34;方法&#34;为了能够使用不同的签名进行调用,*args**kwargs是可行的方法。

答案 3 :(得分:0)

这是我遇到这个问题时正在寻找的解决方案:

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.browser, attr)

t = Test()
t.foo('abc')  # prints 'abc'
t.append('x')  # appends 'x' to t._inner

非常欢迎批评。我想将方法​​添加到Splinter包中的Browser类中,但是它只公开返回实例的函数,而不是类本身。这种方法允许进行伪继承,这意味着我可以声明性地将DOM代码与网站特定的代码分离。 (事后看来,更好的方法可能是直接使用Selenium。)

答案 4 :(得分:0)

以下将响应所有未定义的方法调用:

class Mock:

    def __init__(self, *args, **kwargs):
        pass

    def __getattr__(self, attr):
        def func(*args, **kwargs):
            pass
        return func

或者直接使用 unittest.mock.Mock