首先需要了解的东西
类的实例方法需要是绑定方法,调用的时候才会自动传当前实例作为第一个参数
第一种方法:直接将方法添加到类中
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)