作图在线制作,专业seo优化推广,王者荣耀是哪家公司开发的,莱芜在线下载翻译前想说的话:这是一篇介绍python装饰器的文章#xff0c;对比之前看到的类似介绍装饰器的文章#xff0c;个人认为无人可出其右#xff0c;文章由浅到深#xff0c;由函数介绍到装饰器的高级应用#xff0c;每个介绍必有例子说明。文章太长#xff0c;看完原文后我计划…翻译前想说的话:这是一篇介绍python装饰器的文章对比之前看到的类似介绍装饰器的文章个人认为无人可出其右文章由浅到深由函数介绍到装饰器的高级应用每个介绍必有例子说明。文章太长看完原文后我计划按照文章作者的划分将分为两章翻出来和大家分享,如果你觉得干的还不错就点个赞吧.目录:函数一等对象内部函数从函数中返回函数简单装饰器语法糖复用装饰器装饰器传参从装饰器返回值你是谁?一些现实中的例子时间函数调试代码给代码降速注册插件用户是否登录?有想象力的装饰器装饰类嵌套的装饰器带参数的装饰器BothPlease, But Never Mind the Bread这句话开始我不知道怎么翻直到我看到了维尼熊......,请在这里www.google.com检索Winnie the Pooh BothPlease, But Never Mind the Bread有状态的装饰器类装饰器更多现实中的例子代码降速,重新访问创建单例模式缓存返回值添加单元信息验证JSON正文开始:在本次的装饰器教程中将介绍何为装饰器以及如何创建和使用它们,装饰器提供了简单的语法来调用高阶函数。从定义上讲装饰器是一个函数它接收另一个函数作为参数并且扩展它的功能但不会显式的去修改它说起来可能会让人觉得难理解但它(装饰器)确实不会这么做特别是一会你会看到一些装饰器如何工作的例子函数在理解装饰器之前你首先需要理解函数如何工作。函数会基于给定的参数返回值。这里有一个非常简单的例子: defadd_one(number):...return number 1 add_one(2)3通常情况下函数在python中也会有其它功效而不是仅仅接收输入并返回输出。print()函数是一个例子。在控制台输出的时候它会返回None(1)然而为了理解装饰器将函数认为是接收参数并返回值就足够了注意:在面向函数编程,你几乎只会使用纯函数不会有其它功能然而python不是一个纯函数式语言python支持许多函数式编程概念包括一等对象一等对象在python中函数是一等对象意思是函数可以作为参数被传递就像其它的对象(stringintfloadlist和其它)思考下面的三个函数defsay_hello(name):return fHello {name}defbe_awesome(name):return fYo {name}, together we are the awesomest!defgreet_bob(greeter_func):return greeter_func(Bob)在这里,say_hello()和be_awsone()是常规函数接收一个name参数返回一个字符串然而greet_bob()函数接收一个函数作为他的参数我们可以将say_hello()或者be_awesome()函数传递给它greet_bob(say_hello)Hello Bobgreet_bob(be_awesome)Yo Bob, together we are the awesomest!注意greet_bob(say_hello) 涉及到两个函数但是不同的是:greet_bob()和say_hello,say_hello函数并没有使用()代表只传递了对函数的引用函数没有运行greet_bob()函数是使用了括号所以它会被正常调用内部函数在函数内定义函数是被允许的。这类函数被称为内部函数这里有一个函数和两个内函数的例子defparent():print(Printing from the parent() function)deffirst_child():print(Printing from the first_child() function)defsecond_child():print(Printing from the second_child() function)second_child()first_child()当你调用parent()的时候会发生什么? 请考虑一分钟。会出现下面的输出结果parent()Printingfromthe parent() functionPrintingfromthe second_child() functionPrintingfrom the first_child() function注意内部函数定义的顺序无关紧要和其它的函数一样打印只会发生在内部函数运行的时候而且内部函数在父函数被调用之前不会生效它们的局部作用域是父()它们只作为局部变量存在在父()函数的内部尝试调用first_child()你会得到下面的错误Traceback (most recent call last):File, line 1, in NameError: namefirst_child is not defined不管你何时调用parent()内部函数first_child()和second_child()都会被调用因为它们的局部作用域它们无法再parent()函数外使用从函数中返回函数python允许使用函数来作为返回值下面的例子从外部的父函数parent()返回了一个内部函数defparent(num):deffirst_child():return Hi, I am Emmadefsecond_child():return Call me Liamif num 1:returnfirst_childelse:return second_chil注意这里返回的first_child是没有括号的也就是返回了对函数first_child的引用 带括号的first_child() 指的是对函数求值的结果这个可以在下面的实例中看到 first parent(1) second parent(2)first.first_child at 0x7f599f1e2e18second.second_child at 0x7f599dad5268这个输出代表first变量引用了在parent()中的本地函数first_child()second则指向了second_child()你现在可以像常规函数一样使用first和second虽然他们指向的函数无法被直接访问first()Hi, I am Emmasecond()Call me Liam请注意在前面的例子中我们在父函数中运行内部函数例如first_child()然后在最后的例子中返回的时候没有给内部函数first_child添加括号。这样就获取了将来可以调用的函数的引用。这样有意义吗?简单装饰器现在你已经看到函数和python中的其它对象一样你已经准备好前进来认识python装饰器让我们以一个例子开始:defmy_decorator(func):defwrapper():print(Something is happening before the function is called.)func()print(Something is happening after the function is called.)returnwrapperdefsay_whee():print(Whee!)say_whee my_decorator(say_whee)你能猜到当你调用say_whee()的时候回发生什么么试一下:say_whee()Somethingis happening before the function iscalled.Whee!Somethingis happening after the function is called.要理解这里发生了什么需要回看下之前的例子我们只是应用了你到目前为止学到的所有东西所谓的装饰器发生在下面这行:say_whee my_decorator(say_whee)事实上say_whee现在指向了内部函数wrapper()当你调用my_decorator(say_whee)的时候会将wrapper作为函数返回say_whee.wrapper at 0x7f3c5dfd42f0wrapper()引用原始的say_whee()作为func在两个print()之间调用这个函数简而言之:装饰器包裹一个函数并改变它的行为在继续之前让我们看下第二个例子。因为wrapper()是一个常规的函数装饰器可以以一种动态的方式来修改函数。为了不打扰你的邻居下面的示例演示只会在白天运行的装饰器from datetime importdatetimedefnot_during_the_night(func):defwrapper():if 7 datetime.now().hour 22:func()else:pass #Hush, the neighbors are asleepreturnwrapperdefsay_whee():print(Whee!)say_whee not_during_the_night(say_whee)如果你在睡觉的时间调用say_whee()不会发生任何事情say_whee()语法糖上面的装饰器say_whee()用起来有一点笨拙。首先你键入了三次say_whee另外装饰器隐藏在了函数的定义之下作为替代python允许你使用symbol的方式使用装饰器有时被称为pie语法下面的例子和之前第一个装饰器做了同样的事情defmy_decorator(func):defwrapper():print(Something is happening before the function is called.)func()print(Something is happening after the function is called.)returnwrappermy_decoratordefsay_whee():print(Whee!)所以my_decorator 只是say_whee my_decorator(say_whee)的一种快捷方式这就是如何将装饰器应用到函数上复用装饰器回想一下装饰器只是一个普通的函数。所有常用的工具都是方便重复利用的让我们将装饰器移动到他自己的模型上以便于在其它的函数上使用下面创建了一个decorators.pydefdo_twice(func):defwrapper_do_twice():func()func()return wrapper_do_twice注意:你可以随意定义内部函数的名称通常像wrapper()用起来是没问题的。你在这篇文章中会遇到许多装饰器。为了区别开它们我们将使用decorator名称来命名内部函数但会加上wrapper_前缀。你可以使用常规导入来使用一个新的装饰器from decorators importdo_twicedo_twicedefsay_whee():print(Whee!)当你运行这个例子你会看到原始韩式say_whee()执行两次say_whee()Whee!Whee!装饰器传参如果你有一个函数需要接收一些参数这时候还可以再使用装饰器么然我们试试from decorators importdo_twicedo_twicedefgreet(name):print(fHello {name})不幸的是运行代码抛出了错误 greet(World)Traceback (most recent call last):File, line 1, in TypeError: wrapper_do_twice() takes 0 positional arguments but1 was given问题在于内部函数wrapper_do_twice()没有接收任何参数但是nameWorld却传给了它。你可以让wrapper_do_twice()接收一个参数来修补这个问题但是这样前面的say_whee()函数就无法工作了解决方案是在内部函数使用*args和**kwargs 这样它会允许接收任意个关键参数下面重写了decorators.pydefdo_twice(func):def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)func(*args, **kwargs)return wrapper_do_twice内部函数wrapper_do_twice()现在接收任意数量的参数并会传递给装饰的函数目前say_whee()和greet()都会正常工作say_whee()Whee!Whee! greet(World)Hello WorldHello World从装饰器返回值被装饰的函数返回值会发生什么这会由装饰器来决定我们下面有一个简单的装饰器函数from decorators importdo_twicedo_twicedefreturn_greeting(name):print(Creating greeting)return fHi {name}尝试运行它: hi_adam return_greeting(Adam)Creating greetingCreating greeting print(hi_adam)None装饰器吃掉了从函数返回的值因为do_twice_wrapper()没有返回值调用 return_greeting(Adam) 最后返回了None修复的方式是,需要确认装饰器返回它装饰的函数的值改变decorators.py文件:defdo_twice(func):def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)return func(*args, **kwargs)return wrapper_do_twice执行这个函数返回的值: return_greeting(Adam)Creating greetingCreating greetingHi Adam你是谁?在使用Python(尤其是在交互式shell中)时强大的内省是非常方便的功能。内省是对象在运行时了解其自身属性的能力。例如函数知道自己的名称和文档: print print.__name__print help(print)Help on built-in function print inmodule builtins:print(...)内省同样适用于你自定义的函数:say_whee.wrapper_do_twice at 0x7f43700e52f0 say_whee.__name__wrapper_do_twicehelp(say_whee)Help on function wrapper_do_twiceinmodule decorators:wrapper_do_twice()然而在被装饰后say_whee()会对自身感到疑惑。它现在显示为 do_twice()装饰器的内部函数 wrapper_do_twice()为了修复这个装饰器需要使用functools.wraps装饰器它会保留原始函数的信息再次更新下decorators.py:importfunctoolsdefdo_twice(func):functools.wraps(func)def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)return func(*args, **kwargs)return wrapper_do_twice不需要对被装饰的say_whee()函数做任何更改:say_whee say_whee.__name__say_wheehelp(say_whee)Help on function say_wheeinmodule whee:say_whee()非常好现在say_whee()在被装饰后可以保持自己技术细节:funtools.wraps 装饰器使用函数functools.update_wrapper()来更新指定的属性像__name__和__doc__来用于自省一些现实中的例子让我们看一些用处更大的装饰器例子。你会注意到他们主要的模式和你现在所学的都是一样的importfunctoolsdefdecorator(func):functools.wraps(func)def wrapper_decorator(*args, **kwargs):#Do something beforevalue func(*args, **kwargs)#Do something afterreturnvaluereturn wrapper_decorator对于构建更复杂的装饰器这个是一个很好的模板时间函数让我们从timer装饰器开始它会测量函数运行的时间并且打印持续时间到控制台这是代码:importfunctoolsimporttimedeftimer(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)returnvaluereturnwrapper_timertimerdefwaste_some_time(num_times):for _ inrange(num_times):sum([i**2 for i in range(10000)])这个函数是在函数运行之前获取时间(#1行)并且在函数运行结束之后获取时间(#2行)我们使用 time.perf_counter() 函数这个函数可以非常好的计算时间间隔。下面是一个示例: waste_some_time(1)Finishedwaste_some_time in 0.0010secs waste_some_time(999)Finishedwaste_some_time in 0.3260 secs自己运行测试下手敲下这里的代码确保你理解它的工作原理。如果不明白也不要担心。装饰器是高级方法试着思考下或者画下流程图注意: 如果你只是想获取函数的运行时间timer 装饰器可以满足。如果你想获取到更精确的数据你应该考虑使用timeit 模块来替代它。它临时禁用了垃圾收集并且运行多次以避免函数快速调用带来的噪音数据调试代码下面的debug函数会在每次调用的时候打印函数被调用的参数和它的返回结果importfunctoolsdefdebug(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}) #4returnvaluereturn wrapper_debugsignature 变量是通过 字符串表示方法 来创建所有的输入参数。下面的数字对应了代码中的注释1、将args创建为列表使用repr修饰2、将kwargs创建为列表使用f-string格式化参数为keyvalue!r表示使用repr()表示值3、args和kwargs转换后会合并在signature变量中使用逗号分隔每个变量4、函数运行结束后会返回值让我们在一个简单的函数中使用装饰器被观察它是如何运行的被装饰的函数只有一个位置参数和一个关键字参数debugdef make_greeting(name, ageNone):if age isNone:return fHowdy {name}!else:return fWhoa {name}! {age} already, you are growing up!注意debug装饰器如何打印make_greeting()函数的signature 和返回值 make_greeting(Benjamin)Calling make_greeting(Benjamin)make_greeting returned Howdy Benjamin!Howdy Benjamin! make_greeting(Richard, age112)Calling make_greeting(Richard, age112)make_greeting returned Whoa Richard! 112 already, you are growing up!Whoa Richard! 112 already, you are growing up! make_greeting(nameDorrisile, age116)Calling make_greeting(nameDorrisile, age116)make_greeting returned Whoa Dorrisile! 116 already, you are growing up!Whoa Dorrisile! 116 already, you are growing up!debug修饰符看起来只是重复了我们刚才写的内容 并不是非常有用。 但当应用到不能直接修改的其它函数时它会更加强大。下面的例子计算了一个数学常数E的近似值importmathfrom decorators importdebug#Apply a decorator to a standard library functionmath.factorial debug(math.factorial)def approximate_e(terms18):return sum(1 / math.factorial(n) for n in range(terms))这个例子还演示了如何将装饰器应用到已经定义了的函数当调用approximate_e()函数你可以看到debug函数在工作: approximate_e(5)Calling factorial(0)factorial returned 1Calling factorial(1)factorial returned 1Calling factorial(2)factorial returned 2Calling factorial(3)factorial returned 6Calling factorial(4)factorial returned 242.708333333333333在这个例子中可以得到一个真实值的近似值e 2.718281828给代码降速下面的例子看起来可能不是很有用。可能最常见的用例是您希望对一个不断检查资源是否存在的函数进行速率限制 。 slow_down decorator在调用被修饰的函数之前会暂停一秒钟importfunctoolsimporttimedefslow_down(func):Sleep 1 second before calling the functionfunctools.wraps(func)def wrapper_slow_down(*args, **kwargs):time.sleep(1)return func(*args, **kwargs)returnwrapper_slow_downslow_downdefcountdown(from_number):if from_number 1:print(Liftoff!)else:print(from_number)countdown(from_number- 1)来看下slow_down装饰器的效果你需要自己运行跑下 countdown(3)321Liftoff!countdown()是一个递归函数。也就是说它是一个调用自身的函数 。注册插件装饰器不是必须要修饰被装饰的函数(这句话不太好翻译看下面的例子理解起来很容易)它还可以简单地注册一个函数并将其解包返回例如可以使用它来创建一个轻量级插件体系结构:importrandomPLUGINSdict()defregister(func):Register a function as a plug-inPLUGINS[func.__name__] funcreturnfuncregisterdefsay_hello(name):return fHello {name}registerdefbe_awesome(name):return fYo {name}, together we are the awesomest!defrandomly_greet(name):greeter, greeter_funcrandom.choice(list(PLUGINS.items()))print(fUsing {greeter!r})return greeter_func(name)register装饰器只是在全局PLUGINS 字典中储存了被装饰函数的引用。注意你不需要在例子中写内部函数或者使用functools.wraps 因为返回的是一个未经过修改的初始函数randomly_greet()函数在注册函数中随机选择一个使用。注意PLUGINS字典已经包含了对注册为插件的每个函数对象的引用:PLUGINS{say_hello: ,be_awesome: } randomly_greet(Alice)Usingsay_helloHello Alice这个插件的主要用处在于不需要再单独维护一个插件列表。这个列表在插件注册时自动创建使得添加一个新插件变得很简单只需定义函数并用register装饰即可。如果你对python中的globals()函数熟悉你可能会看到一些和我们的插件结构相似之处。globals()可以访问当前作用于的所有全局变量包括我们的插件:globals(){...,#Lots of variables not shown here.say_hello: ,be_awesome: ,randomly_greet: }使用register 装饰器可以创建感兴趣的变量管理列表有效地从globals()中筛选出一些函数用户是否登录?在继续讨论一些更有趣的装饰器之前让我们在最后一个示例中演示通常在处理web框架时使用的装饰器。在这个例子中我们使用Flask去设置一个/secret web页面这个页面只对登录用户或者其他有权限的用户展示from flask importFlask, g, request, redirect, url_forimportfunctoolsapp Flask(__name__)deflogin_required(func):Make sure user is logged in before proceedingfunctools.wraps(func)def wrapper_login_required(*args, **kwargs):if g.user isNone:return redirect(url_for(login, nextrequest.url))return func(*args, **kwargs)returnwrapper_login_requiredapp.route(/secret)login_requireddefsecret():...虽然这里演示了如何对web框架添加身份验证吗但通常不应该自己编写这些类型的装饰器。对于Flask可以使用Flask-login扩展这里的功能更丰富也更加安全有想象力的装饰器到目前为止你已经看到了如何创建简单的装饰器并且非常了解什么是装饰器以及它们是如何工作的。请从这篇文章中休息一下练习学到的一切。在本教程的第二部分中我们将探索更高级的特性包括如何使用以下特性:1、在类上使用装饰器(装饰类)2、在一个函数上应用多个装饰器3、带参数的装饰器4、可以选择是否接收参数的装饰器5、带状态的装饰器6、类装饰器