Python从门到精通(八):元类-02-装饰器

网友投稿 254 2022-08-26


Python从门到精通(八):元类-02-装饰器

一、基础用法

1.1、​函数添加装饰器

不会修改原始函数的参数签名和返回值。使用用*args和 **kargs的目的是确保任何参数都能适用。装饰器类似一个AOP程序

import timefrom functools import wrapsdef time_use(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) #func就是原始的函数count_down end = time.time() print(f'func name: {func.__name__}, time use: {end - start} s') #注意这里的__name__写法 return result return wrapper@time_usedef count_down(n): while n > 0: n -= 1count_down(100000)# func name: count_down, time use: 0.005406856536865234 scount_down(10000000)# func name: count_down, time use: 0.5120158195495605 s

​1.2、函数解除装饰器

import timefrom functools import wrapsdef time_use(func): @wraps(func) def wrapper1(*args, **kwargs): start = time.time() result = func(*args, **kwargs) #func就是原始的函数count_down end = time.time() print(f'func name: {func.__name__}, time use: {end - start} s') #注意这里的__name__写法 return result return wrapper1@time_usedef count_down(n): while n > 0: n -= 1count_down(100000)# func name: count_down, time use: 0.005406856536865234 scount_down(10000000)# func name: count_down, time use: 0.5120158195495605 s@time_usedef add(x, y): return x + y#用了这行以后就解除了装饰器,但必须要调用一次否则是不生效的,比如在下面代码中插入orig_add = add.__wrapped__# add(3,5) #这行还是会执行wraps代码print(f'add result: {orig_add(3, 5)}') #add result: 8#这种写法也可以print(f'add result: {add.__wrapped__(3, 5)}')

1.3、得到函数元信息

print(f'func name: {count_down.__name__}')print(f'doc is: {count_down.__doc__}')print(f'annotations is: {count_down.__annotations__}')from inspect import signatureprint(f'signature: {signature(count_down)}')#signature: (*args, **kwargs)

1.4、在类中定义装饰器

这样装饰器可以同时在类的内部和外部一起使用了。

class A: # Decorator as an instance method def decorator_1(self, func): @wraps(func) def wrapper(*args, **kwargs): print('Decorator 1') return func(*args, **kwargs) return wrapper # Decorator as a class method @classmethod def decorator_2(cls, func): @wraps(func) def wrapper(*args, **kwargs): print('Decorator 2') return func(*args, **kwargs) return wrapper# As an instance methoda = A()@a.decorator_1def spam(): pass# As a class method@A.decorator_2def grok(): pass class B(A): @A.decorator_2 def bar(self): pass

class Profiled: def __init__(self, func): wraps(func)(self) self.ncalls = 0 def __call__(self, *args, **kwargs): self.ncalls += 1 return self.__wrapped__(*args, **kwargs)#确保绑定方法对象能正确的创建,types.MethodType创建一个绑定方法,只有当实例被使用时才会被创建。 def __get__(self, instance, cls): if instance is None: return self else: return types.MethodType(self, instance)@Profileddef add(x, y): return x + yclass Spam: @Profiled def bar(self, x): print(f'object: {self}, param is: {x}')print(f'number add result: {add(3, 5)}')print(f'number add result: {add(5, 8)}')print(f'ncalls: {add.ncalls}')# number add result: 8# number add result: 13# ncalls: 2s = Spam()s.bar(1)s.bar(2)s.bar(3)print(f'bar ncalls: {Spam.bar.ncalls}')# object: <__main__.Spam object at 0x10d334f70>, param is: 1# object: <__main__.Spam object at 0x10d334f70>, param is: 2# object: <__main__.Spam object at 0x10d334f70>, param is: 3# bar ncalls: 3s = Spam()def grok(self, x): passprint(f'grok get: {grok.__get__(s, Spam)}')# grok get: >

1.5、运行时设置装饰器参数

from functools import wraps, partialimport logging# Utility decorator to attach a function as an attribute of objdef attach_wrapper(obj, func=None): if func is None: return partial(attach_wrapper, obj) setattr(obj, func.__name__, func) return funcdef logged(level, name=None, message=None): ''' Add logging to a function. level is the logging level, name is the logger name, and message is the log message. If name and message aren't specified, they default to the function's module and name. ''' def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.log(level, logmsg) return func(*args, **kwargs) # Attach setter functions @attach_wrapper(wrapper) def set_level(newlevel): nonlocal level #修改函数内部的变量 level = newlevel @attach_wrapper(wrapper) def set_message(newmsg): nonlocal logmsg logmsg = newmsg return wrapper return decorate# Example use@logged(logging.DEBUG)def add(x, y): return x + y@logged(logging.CRITICAL, 'example')def spam(): print('Spam!')import logginglogging.basicConfig(level=logging.DEBUG)print(f'add result: {add(3, 5)}')add.set_message('Add called') #修改函数内部变量print(f'add result: {add(3, 5)}')add.set_level(logging.WARNING)print(f'add result: {add(3, 5)}')

二、参数相关

2.1、带参数的装饰器

from functools import wrapsimport loggingdef logged(level, name=None, message=None): def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.log(level, message) return func(*args, **kwargs) return wrapper return decorate# Example use@logged(logging.CRITICAL)def add(x, y): return x + yadd(1, 2) #add@logged(logging.CRITICAL, 'example')def spam(): print('Spam!')spam() #spam

2.2、带可选参数的装饰器

from functools import wraps, partialimport loggingdef logged(func=None, *, level=logging.DEBUG, name=None, message=None): if func is None: return partial(logged, level=level, name=name, message=message) logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.log(level, logmsg) return func(*args, **kwargs) return wrapper# Example use@loggeddef add(x, y): return x + y@logged(level=logging.CRITICAL, name='example')def spam(): print('Spam!')

三、装饰器使用

3.1、函数类型检查

from inspect import signaturefrom functools import wrapsdef type_assert(*ty_args, **ty_kwargs): def decorate(func): # If in optimized mode, disable type checking if not __debug__: return func # Map function argument names to supplied types sig = signature(func) #从指定类型到名称的部分绑定,它与bind的区别是不允许忽略任何参数 bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments @wraps(func) def wrapper(*args, **kwargs): bound_values = sig.bind(*args, **kwargs) # Enforce type assertions across supplied arguments for name, value in bound_values.arguments.items(): if name in bound_types: if not isinstance(value, bound_types[name]): raise TypeError(f'Argument {name} must be {bound_types[name]}') return func(*args, **kwargs) return wrapper return decorate@type_assert(int, int)def add(x, y): return x + yprint(f'add result: {add(2, 3)}')# add(2, 'hello')@type_assert(int, z=int)def spam(x, y, z=42): print(f'x = {x}, y = {y}, z = {z}')spam(1, 2, 3)spam(1, 'hello', 3)# spam(1, 'hello', 'world')def decorate(func): # If in optimized mode, disable type checking if not __debug__: return funcfrom inspect import signaturedef spam(x, y, z=42): passsig = signature(spam)print(f'sig = {sig}')print(f'parameters: {sig.parameters}')print(f'parameters z name = {sig.parameters["z"].name}')print(f'parameters z default = {sig.parameters["z"].default}')print(f'parameters z kind = {sig.parameters["z"].kind}')bound_types = sig.bind_partial(int, z=int)print(f'bound_types = {bound_types}')print(f'bound_types arguments = {bound_types.arguments}')bound_values = sig.bind(1, 2, 3)print(f'arguments = {bound_values.arguments}')for name, value in bound_values.arguments.items(): if name in bound_types.arguments: if not isinstance(value, bound_types.arguments[name]): raise TypeError@type_assert(int, list)def bar(x, items=None): if items is None: items = [] items.append(x) return itemsprint(f'bar single param: {bar(3)}')# print(f'bar double param: {bar(3, 5)}')print(f'bar mix param: {bar(4, [1, 2, 3])}')@type_assertdef spam(x:int, y, z:int = 42): print(x,y,z)

add result: 5x = 1, y = 2, z = 3x = 1, y = hello, z = 3sig = (x, y, z=42)parameters: OrderedDict([('x', ), ('y', ), ('z', )])parameters z name = zparameters z default = 42parameters z kind = POSITIONAL_OR_KEYWORDbound_types = , z=)>bound_types arguments = {'x': , 'z': }arguments = {'x': 1, 'y': 2, 'z': 3}bar single param: [3]bar mix param: [1, 2, 3, 4]

3.2、给函数添加参数

from functools import wrapsdef optional_debug(func): @wraps(func) def wrapper(*args, debug=False, **kwargs): if debug: print('Calling', func.__name__) return func(*args, **kwargs) return wrapper@optional_debugdef spam(a, b, c): print(f'a = {a}, b = {b}, c = {c}')spam(1, 2, 3)spam(1, 2, 3, debug=True)# a = 1, b = 2, c = 3# Calling spam# a = 1, b = 2, c = 3from functools import wrapsimport inspectdef optional_debug(func): if 'debug' in inspect.getfullargspec(func): raise TypeError('debug argument already defined') @wraps(func) def wrapper(*args, debug=False, **kwargs): if debug: print(f'Calling {func.__name__}') return func(*args, **kwargs) sig = inspect.signature(func) parms = list(sig.parameters.values()) parms.append(inspect.Parameter('debug', inspect.Parameter.KEYWORD_ONLY, default=False)) wrapper.__signature__ = sig.replace(parameters=parms) return wrapper@optional_debugdef add(x,y): return x+yprint(f'signature: {inspect.signature(add)}')print(f'add result: {add(5,3)}')# signature: (x, y, *, debug=False)# add result: 8

3.3、扩充类的功能

def log_getattribute(cls): # Get the original implementation orig_getattribute = cls.__getattribute__ # Make a new definition def new_getattribute(self, name): print(f'getting name: {name}') return orig_getattribute(self, name) # Attach to the class and return cls.__getattribute__ = new_getattribute return cls# Example use@log_getattributeclass A: def __init__(self,x): self.x = x def spam(self): passa = A(30)print(f'a.x = {a.x}')print(f'a.spam(): {a.spam()}')# getting name: x# a.x = 30# getting name: spam# a.spam(): Noneclass LoggedGetattribute: def __getattribute__(self, name): print(f'getting name: {name}') return super().__getattribute__(name)# Example:class A(LoggedGetattribute): def __init__(self,x): self.x = x def spam(self): pass


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:python对字符串内容去重(输出去重后的字符串 python)
下一篇:提交gRPC
相关文章

 发表评论

暂时没有评论,来抢沙发吧~