网站遮罩是什么,游戏软件开发公司排名,建网站选服务器,wordpress实现静态化什么是python装饰器#xff1f;
顾名思义#xff0c;从字面意思就可以理解#xff0c;它是用来装饰Python的工具#xff0c;使得代码更具有Python简洁的风格。换句话说#xff0c;它是一种函数的函数#xff0c;因为装饰器传入的参数就是一个函数#xff0…什么是python装饰器
顾名思义从字面意思就可以理解它是用来装饰Python的工具使得代码更具有Python简洁的风格。换句话说它是一种函数的函数因为装饰器传入的参数就是一个函数然后通过实现各种功能来对这个函数的功能进行增强。
为什么要使用装饰器
装饰器是通过某种方式来增强函数的功能。当然我们可以通过很多方式来增强函数的功能只是装饰器有一个无法替代的优势--简洁。只需要在每个函数上方加一个就可以对这个函数进行增强。
装饰器Decorator是Python中一个重要部分它本质上是一个函数不同于普通函数装饰器的返回值是一个函数对象。通过利用装饰器我们可以让其他函数在不做任何代码改动的情况下增加额外的功能同时也能够让代码更加简洁。
装饰器
装饰器是Python的一种高级函数它可以接受一个函数作为参数并返回一个新的函数。通过使用装饰器我们可以在不修改原函数代码的情况下为函数添加额外的功能或行为。
基本定义
装饰器的定义格式如下
def decorator_function(original_function):def wrapper_function(*args, **kwargs):# 在调用原函数之前的额外操作result original_function(*args, **kwargs)# 在调用原函数之后的额外操作return resultreturn wrapper_function
在上述示例中decorator_function是装饰器函数它接受一个原函数original_function作为参数并返回一个新的函数wrapper_function。wrapper_function内部可以执行一些在调用原函数之前或之后的额外操作。
def a_new_decorator(a_func):def wrapTheFunction():print(I am doing some boring work before executing a_func())a_func()print(I am doing some boring work after executing a_func())return wrapTheFunctiondef a_function_requiring_decoration():print(I am the function which needs some decoration to remove my foul smell)a_function_requiring_decoration()
#outputs: I am the function which needs some decoration to remove my foul smella_function_requiring_decoration a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
上面的例子演示了装饰器的工作过程Python提供了语法糖来简化装饰器在函数定义时加上装饰器在函数被调用时自动会调用装饰器返回的函数
a_new_decorator
def a_function_requiring_decoration():Hey you! Decorate me!print(I am the function which needs some decoration to remove my foul smell)a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()#the a_new_decorator is just a short way of saying:
a_function_requiring_decoration a_new_decorator(a_function_requiring_decoration)
原函数参数
如果被装饰器包装的函数带有参数需要通过*args, **kwargs来传递参数*args, **kwargs可以传递任意数量的位置参数和关键字参数。
def do_twice(func):def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)func(*args, **kwargs)return wrapper_do_twicedo_twice
def say_hello(name):print(fHello {name}!)say_hello(Jack’)‘’’
Hello Jack!
Hello Jack!
’‘’
装饰器返回值
装饰器如果要返回原函数的返回值需要用一个返回变量接收原函数的返回值
def log_foo(func):def wrapper(*args, **kwargs):print(ffunc() start...)result func(*args, **kwargs)print(ffunc() end.)return resultreturn wrapperlog_foo
def say_hello(name):print(fHello {name}!)say_hello(Jack’)‘’
func() start...
Hello Jack!
func() end.
‘
保留原函数信息
函数携带的一些基本信息例如函数名、函数文档等我们可以通过func.__name__获取函数名、可以通过func.__doc__获取函数的文档信息用户也可以通过注解等方式为函数添加元信息。
由于包装后的函数是wrapper函数名的等信息都修改为wrapper的信息。
def log_foo(func):def wrapper(*args, **kwargs):自定义日志装饰器print(ffunc() start...)result func(*args, **kwargs)print(ffunc() end.)return resultreturn wrapperlog_foo
def say_hello(name):say_hello函数print(fHello {name}!)print(say_hello.__name__)
print(say_hello.__doc__)
print(help(say_hello)) ‘’’
wrapper自定义日志装饰器Help on function wrapper in module __main__:wrapper(*args, **kwargs)自定义日志装饰器None
’‘’
为了解决这个问题使用functools.wraps修饰修饰器这将保留有关原始功能的信息。
import functools
def log_foo(func):functools.wraps(func)def wrapper(*args, **kwargs):自定义日志装饰器print(ffunc() start...)result func(*args, **kwargs)print(ffunc() end.)return resultreturn wrapperlog_foo
def say_hello(name):say_hello函数print(fHello {name}!)print(say_hello.__name__)
print(say_hello.__doc__)
print(help(say_hello))‘’’
say_hellosay_hello函数Help on function say_hello in module __main__:say_hello(name)say_hello函数None
’‘’
多个装饰器装饰
可以多个装饰器同时装饰一个函数
def dec_foo1(func):functools.wraps(func)def wrapper(*args, **kwargs):print(f--dec_foo1() start--)result func(*args, **kwargs)print(f--dec_foo1() end--)return resultreturn wrapperdef dec_foo2(func):functools.wraps(func)def wrapper(*args, **kwargs):print(f--dec_foo2() start--)result func(*args, **kwargs)print(f--dec_foo2() end--)return resultreturn wrapperdec_foo1
dec_foo2
def say_hello(name):say_hello函数print(fHello {name}!)say_hello(Rose’)‘’
--dec_foo1() start--
--dec_foo2() start--
Hello Rose!
--dec_foo2() end--
--dec_foo1() end--
‘
装饰器函数包装的顺序是从上往下。 带参数的装饰器
能将参数传递给装饰器是很有用的比如我们可以给dec_foo1()扩展为dec_foo1(num_times)。
注意代码多内置了一层函数传参需要嵌套3层函数前面的都是2层通过闭包的形式将参数num_times传入内层函数可以理解为dec_foo1(num_times)返回的函数再来装饰func。
def dec_foo1(num_times):def decorator_repeat(func):functools.wraps(func)def wrapper(*args, **kwargs):print(f--dec_foo1() start--)for _ in range(num_times):result func(*args, **kwargs)print(f--dec_foo1() end--)return resultreturn wrapperreturn decorator_repeatdec_foo1(4)
def say_hello(name):print(fHello {name}!)say_hello(Rose’)‘’
--dec_foo1() start--
Hello Rose!
Hello Rose!
Hello Rose!
Hello Rose!
--dec_foo1() end--
‘
装饰器类
前面介绍类对象只要实现了__call__也可以作为函数对象被调用函数对象也可以用来作为装饰器。
类的装饰器在声明的时候需要使用cls()来声明因为这样才能正确的生成一个函数对象不能简单的使用类名来声明装饰器那样会出错TypeError
from functools import wrapsclass logit(object):def __init__(self, logfileout.log):self.logfile logfiledef __call__(self, func):wraps(func)def wrapped_function(*args, **kwargs):log_string func.__name__ was calledprint(log_string)# 打开logfile并写入with open(self.logfile, a) as opened_file:# 现在将日志打到指定的文件opened_file.write(log_string \n)# 现在发送一个通知self.notify()return func(*args, **kwargs)return wrapped_functiondef notify(self):# logit只打日志不做别的passlogit() #将类实例化
def myfunc1():passmyfunc1()
并且通过函数对象实现装饰器有一个优势便是可以提供更多的预置的变量。
给 logit 创建子类来添加 email 的功能。
class email_logit(logit):一个logit的实现版本可以在函数调用时发送email给管理员def __init__(self, emailadminmyproject.com, *args, **kwargs):self.email emailsuper(email_logit, self).__init__(*args, **kwargs)def notify(self):# 发送一封email到self.email# 这里就不做实现了pass
email_logit() 将会和 logit() 产生同样的效果但是在打日志的基础上还会多发送一封邮件给管理员。 常见用法
类的装饰器 from dataclasses import dataclassdataclass
class PlayingCard:rank: strsuit: str 类方法装饰器
类中修饰器的一种用法是装饰类中的函数有一些常用的python内置的修饰器。
property
修饰函数可以作为属性使用与所定义的属性配合使用这样可以防止属性被修改。
class House:def __init__(self, price):self._price pricepropertydef price(self):return self._priceprice.setterdef price(self, new_price):if new_price 0 and isinstance(new_price, float):self._price new_priceelse:print(Please enter a valid price)price.deleterdef price(self):del self._price
按照惯例在python中当在变量名前加一个下划线时意味着告诉其他开发人员不应直接在类外访问或者修改改变量。
property 获取属性。price.setter 属性设定可以用来限制属性的范围等等。price.deleter 定义属性的删除在del house.price时被执行。
abstractmethod
抽象方法表示基类的一个方法没有实现所以基类不能实例化子类实现了该抽象方法才能被实例化。
classmethod
classmethod声明方法为类方法,直接通过 类或者实例.类方法调用。经过classmethod修饰的方法不需要self参数但是需要一个标识类本身的cls参数。
class T:classmethoddef class_test(cls):#必须有cls参数print i am a class method
if __name__ __main__:T.class_test()T().class_test()
staticmethoed
声明方法为静态方法直接通过 类或者实例.静态方法调用。经过staticmethod修饰的方法不需要self参数其使用方法和直接调用函数一样。 记录状态的装饰器
装饰器可以跟踪函数的状态如下是一个记录函数调用次数的装饰器通过函数属性来计数。
import functoolsdef count_calls(func):functools.wraps(func)def wrapper_count_calls(*args, **kwargs):wrapper_count_calls.num_calls 1print(fCall {wrapper_count_calls.num_calls} of {func.__name__!r})return func(*args, **kwargs)wrapper_count_calls.num_calls 0return wrapper_count_callscount_calls
def say_whee():print(Whee!)
记录状态的最好的方式是使用类作为装饰器只需要实现__init__()和__call__()。使用functools.update_wrapper(self, func)保留信息。
import functoolsclass CountCalls:def __init__(self, func):functools.update_wrapper(self, func)self.func funcself.num_calls 0def __call__(self, *args, **kwargs):self.num_calls 1print(fCall {self.num_calls} of {self.func.__name__!r})return self.func(*args, **kwargs)CountCalls
def say_whee():print(Whee!)
timer
timer装饰器记录函数执行的时间。
import functools
import timedef timer(func):Print the runtime of the decorated functionfunctools.wraps(func)def wrapper_timer(*args, **kwargs):start_time time.perf_counter() # 1value func(*args, **kwargs)end_time time.perf_counter() # 2run_time end_time - start_time # 3print(fFinished {func.__name__!r} in {run_time:.4f} secs)return valuereturn wrapper_timertimer
def waste_some_time(num_times):for _ in range(num_times):sum([i**2 for i in range(10000)])waste_some_time(999)
#Finished waste_some_time in 1.6487 secsdebug
debug装饰器输出调试信息帮助程序员分析问题。
import functoolsdef debug(func):Print the function signature and return valuefunctools.wraps(func)def wrapper_debug(*args, **kwargs):args_repr [repr(a) for a in args] # 1kwargs_repr [f{k}{v!r} for k, v in kwargs.items()] # 2signature , .join(args_repr kwargs_repr) # 3print(fCalling {func.__name__}({signature}))value func(*args, **kwargs)print(f{func.__name__!r} returned {value!r}) # 4return valuereturn wrapper_debugdebug
def make_greeting(name, ageNone):if age is None:return fHowdy {name}!else:return fWhoa {name}! {age} already, you are growing up! make_greeting(Benjamin)
Calling make_greeting(Benjamin)
make_greeting returned Howdy Benjamin!
Howdy Benjamin!
授权
装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权
from functools import wrapsdef requires_auth(f):wraps(f)def decorated(*args, **kwargs):auth request.authorizationif not auth or not check_auth(auth.username, auth.password):authenticate()return f(*args, **kwargs)return decorated
日志
日志是装饰器运用的另一个亮点
rom functools import wrapsdef logit(func):wraps(func)def with_logging(*args, **kwargs):print(func.__name__ was called)return func(*args, **kwargs)return with_logginglogit
def addition_func(x):Do some math.return x xresult addition_func(4)
# Output: addition_func was called
另外一个例子
import logging
from functools import partial
def wrapper_property(obj, funcNone):if func is None:return partial(wrapper_property, obj)setattr(obj, func.__name__, func)return func
def logger_info(level, nameNone, messageNone):def decorate(func):logmsg message if message else func.__name__def wrapper(*args, **kwargs):log.log(level, logmsg)return func(*args, **kwargs)
wrapper_property(wrapper)def set_level(newlevel):nonlocal levellevel newlevel
wrapper_property(wrapper)def set_message(newmsg):nonlocal logmsglogmsg newmsg
return wrapper
return decorate
logger_info(logging.WARNING)
def main(x, y):return x ymain(3, 3)
# 输出
# WARNING:Test:main
# 6main.set_level(logging.ERROR)
main(5, 5)# 输出
# ERROR:Test:main
# 10
这里面最重要的是wrapper_property这个函数它的功能是把一个函数func变成一个对象obj的属性然后通过调用wrapper_property给装饰器添加了两个属性set_message和set_level分别用于改变输出日志的内容和改变输出日志的等级。 Registering Plugins
装饰器不必包装他们正在装饰的功能它们还可以简单地注册一个函数的存在并将其解封返回。如下是创建轻量级插件架构的代码。
import random
PLUGINS dict()def register(func):Register a function as a plug-inPLUGINS[func.__name__] funcreturn funcregister
def say_hello(name):return fHello {name}register
def be_awesome(name):return fYo {name}, together we are the awesomest!def randomly_greet(name):greeter, greeter_func random.choice(list(PLUGINS.items()))print(fUsing {greeter!r})return greeter_func(name) 创建单例Singletons
单例是一种设计模式单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例。通过保存实例使得每次返回的是同一个对象实例。
import functoolsdef singleton(cls):Make a class a Singleton class (only one instance)functools.wraps(cls)def wrapper_singleton(*args, **kwargs):if not wrapper_singleton.instance:wrapper_singleton.instance cls(*args, **kwargs)return wrapper_singleton.instancewrapper_singleton.instance Nonereturn wrapper_singletonsingleton
class TheOne:pass
实现缓存和记忆机制
通过记录状态来保存之前的计算结果。
import functools
from decorators import count_callsdef cache(func):Keep a cache of previous function callsfunctools.wraps(func)def wrapper_cache(*args, **kwargs):cache_key args tuple(kwargs.items())if cache_key not in wrapper_cache.cache:wrapper_cache.cache[cache_key] func(*args, **kwargs)return wrapper_cache.cache[cache_key]wrapper_cache.cache dict()return wrapper_cachecache
count_calls
def fibonacci(num):if num 2:return numreturn fibonacci(num - 1) fibonacci(num - 2)
添加额外的信息
通过在装饰函数时给函数附加属性来添加信息。
def set_unit(unit):Register a unit on a functiondef decorator_set_unit(func):func.unit unitreturn funcreturn decorator_set_unitimport mathset_unit(cm^3)
def volume(radius, height):return math.pi * radius**2 * height volume(3, 5)
141.3716694115407 volume.unit
cm^3