类装饰器

首先需要了解的东西


类的实例方法需要是绑定方法,调用的时候才会自动传当前实例作为第一个参数

第一种方法:直接将方法添加到类中

In [50]: class Student:
    ...:     pass
    ...: 

In [51]: def get_age(self):
    ...:     return 18
    ...: 

In [52]: Student.get_age = get_age

In [53]: Student().get_age
Out[53]: <bound method get_age of <__main__.Student object at 0x7fb554eed278>>

第二种方法:使用MethodType

In [1]: import types
In [2]: class Student:
   ...:     pass
   ...: 

In [3]: def get_age(self):
   ...:     return 18
   ...: 

In [4]: s1 = Student()

In [5]: s1.get_age = types.MethodType(get_age, s1)

In [6]: s1.get_age
Out[6]: <bound method get_age of <__main__.Student object at 0x7f807ec0b828>>

In [7]: s2 = Student()

In [8]: s2.get_age    # 只绑定到实例s1上
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-9127fb2f5838> in <module>()
----> 1 s2.get_age

AttributeError: 'Student' object has no attribute 'get_age'                                                                                                                                                                                

 MethodType接收两个参数

class MethodType:
    __func__ = ...  # type: _StaticFunctionType
    __self__ = ...  # type: object
    __name__ = ...  # type: str
    def __init__(self, func: Callable, obj: object) -> None: ...
    def __call__(self, *args: Any, **kwargs: Any) -> Any: ...

问题 


 

需要写一个装饰器对类方法进行异常捕获

装饰器1:

def outer_func(func):
    def inner_func(*args, **kw):
        try:
            response = func(*args, **kw)
        except Exception as e:
            return None
        return response
    return inner_func

可以正常使用

装饰器2:

class HandleException2:
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            try:
                response = func(*args, **kwargs)
            except Exception as e:
                print(e)
                return None
            return response
        return wrapper

这样子也可以正常使用

@HandleException2()
def func(self):
...

装饰器3:

class HandleException:
    def __init__(self, func):
        self._func = func
        # functools.wraps(func)(self)

    def __call__(self, *args, **kw):
        print('call __call__')
        try:
            response = self._func(*args, **kw)
        except Exception as e:
            print(e)
            return None
        return response

@HandleException
def func(self):
...

这样子写调用的时候出现错误

func() missing 1 required positional argument: 'self'

接下来就是瞎扯了

如果装饰器返回的是一个方法,就可以正常运行,上面1和2两个装饰器返回的都是方法,装饰完之后还是绑定方法

>>>rh.handle_teacher_response
<bound method outer_func.<locals>.inner_func of <common.resources_handle.ResourcesHandle object at 0x7fb45b23e630>>

但如果返回的是一个类就有问题了,第三个装饰器装饰完之后仅仅是一个类实例

>>>rh.handle_teacher_response
<common.resources_handle.HandleException object at 0x7f134524d630>

实例调用handle_teacher_response的时候并不会传实例本身作为第一个参数,所以报了缺少参数的异常

解决方法:加一个__get__
class HandleException:
    def __init__(self, func):
        self._func = func
        # functools.wraps(func)(self)

    def __call__(self, *args, **kw):
        print('call __call__')
        try:
            response = self._func(*args, **kw)
        except Exception as e:
            print(e)
            return None
        return response

    def __get__(self, instance, cls):
        print('call __get__')
        if instance is None:
            return self
        else:
            print(types.MethodType(self, instance))
            return types.MethodType(self, instance)

下面真的瞎猜了

Class().func()的时候先调用__get__返回一个绑定方法,再调用__call__方法

如果是类直接调用func,__get__返回的是HandleException本身,跟没写__get__应该是一样的,调用的时候应该需要主动传一个参数,这么写Class.func(object)

原文地址:https://www.cnblogs.com/songbird/p/8467243.html