绍兴网站推广,什么软件能自己做网站,做盗版影视网站,做英文企业网站Python有大量强大又贴心的特性#xff0c;如果要列个最受欢迎排行榜#xff0c;那么装饰器绝对会在其中。刚接触装饰器#xff0c;会觉得代码不多却难以理解。其实装饰器的语法本身挺简单的#xff0c;复杂是因为同时混杂了其它的概念。下面我们一起抛去无关概念#xff0…Python有大量强大又贴心的特性如果要列个最受欢迎排行榜那么装饰器绝对会在其中。刚接触装饰器会觉得代码不多却难以理解。其实装饰器的语法本身挺简单的复杂是因为同时混杂了其它的概念。下面我们一起抛去无关概念简单地理解下Python的装饰器。装饰器的原理在解释器下跑个装饰器的例子直观地感受一下# make_bold就是装饰器实现方式这里略去 make_bold... def get_content():... return hello world... get_content()hello world被make_bold装饰的get_content调用后返回结果会自动被b标签包住。怎么做到的呢简单4步就能明白了。1. 函数是对象我们定义个get_content函数。这时get_content也是个对象它能做所有对象的操作。它有id有type有值。 id(get_content)140090200473112 type(get_content) get_content跟其他对象一样可以被赋值给其它变量。 func_name get_content func_name()hello world它可以当参数传递也可以当返回值 def foo(bar):... print(bar())... return bar... func foo(get_content)hello world func()hello world2. 自定义函数对象我们可以用class来构造函数对象。有成员函数__call__的就是函数对象了函数对象被调用时正是调用的__call__。class FuncObj(object):def __init__(self, name):print(Initialize)self.name namedef __call__(self):print(Hi, self.name)我们来调用看看。可以看到函数对象的使用分两步构造和调用(同学们注意了这是考点)。 fo FuncObj(python)Initialize fo()Hi python3. 是个语法糖装饰器的没有做什么特别的事不用它也可以实现一样的功能只不过需要更多的代码。make_bolddef get_content():return hello world上面的代码等价于下面的def get_content():return hello worldget_content make_bold(get_content)make_bold是个函数要求入参是函数对象返回值是函数对象。的语法糖其实是省去了上面最后一行代码使可读性更好。用了装饰器后每次调用get_content真正调用的是make_bold返回的函数对象。4. 用类实现装饰器入参是函数对象返回是函数对象如果第2步里的类的构造函数改成入参是个函数对象不就正好符合要求吗我们来试试实现make_bold。class make_bold(object):def __init__(self, func):print(Initialize)self.func funcdef __call__(self):print(Call)return {}.format(self.func())大功告成看看能不能用。 make_bold... def get_content():... return hello world...Initialize get_content()Callhello world成功实现装饰器是不是很简单这里分析一下之前强调的构造和调用两个过程。我们去掉语法糖好理解一些。# 构造使用装饰器时构造函数对象调用了__init__ get_content make_bold(get_content)Initialize# 调用实际上直接调用的是make_bold构造出来的函数对象 get_content()Callhello world到这里就彻底清楚了完结撒花可以关掉网页了~~~(如果只是想知道装饰器原理的话)函数版装饰器阅读源码时经常见到用嵌套函数实现的装饰器怎么理解同样仅需4步。1. def的函数对象初始化用class实现的函数对象很容易看到什么时候构造的那def定义的函数对象什么时候构造的呢# 这里的全局变量删去了无关的内容 globals(){} def func():... pass... globals(){func: }不像一些编译型语言程序在启动时函数已经构造那好了。上面的例子可以看到执行到def会才构造出一个函数对象并赋值给变量make_bold。这段代码和下面的代码效果是很像的。class NoName(object):def __call__(self):passfunc NoName()2. 嵌套函数Python的函数可以嵌套定义。def outer():print(Before def:, locals())def inner():passprint(After def:, locals())return inner#inner是在outer内定义的所以算outer的局部变量。#执行到def inner时函数对象才创建因此每次调用outer都会创建一个新的inner。#下面可以看出每次返回的inner是不同的。 outer()Before def: {}After def: {inner: .inner at 0x7f0b18fa0048}.inner at 0x7f0b18fa0048 outer()Before def: {}After def: {inner: .inner at 0x7f0b18fa00d0}.inner at 0x7f0b18fa00d03. 闭包嵌套函数有什么特别之处因为有闭包。def outer():msg hello worlddef inner():print(msg)return inner下面的试验表明inner可以访问到outer的局部变量msg。 func outer() func()hello world闭包有2个特点inner能访问outer及其祖先函数的命名空间内的变量(局部变量函数参数)。调用outer已经返回了但是它的命名空间被返回的inner对象引用所以还不会被回收。这部分想深入可以去了解Python的LEGB规则。4. 用函数实现装饰器装饰器要求入参是函数对象返回值是函数对象嵌套函数完全能胜任。def make_bold(func):print(Initialize)def wrapper():print(Call)return {}.format(func())return wrapper用法跟类实现的装饰器一样。可以去掉语法糖分析下构造和调用的时机。 make_bold... def get_content():... return hello world...Initialize get_content()Callhello world因为返回的wrapper还在引用着所以存在于make_bold命名空间的func不会消失。make_bold可以装饰多个函数wrapper不会调用混淆因为每次调用make_bold都会有创建新的命名空间和新的wrapper。到此函数实现装饰器也理清楚了完结撒花可以关掉网页了~~~(后面是使用装饰的常见问题)常见问题1. 怎么实现带参数的装饰器带参数的装饰器有时会异常的好用。我们看个例子。 make_header(2)... def get_content():... return hello world... get_content()hello world#怎么做到的呢其实这跟装饰器语法没什么关系。去掉语法糖会变得很容易理解。make_header(2)def get_content():return hello world# 等价于def get_content():return hello worldunnamed_decorator make_header(2)get_content unnamed_decorator(get_content)上面代码中的unnamed_decorator才是真正的装饰器make_header是个普通的函数它的返回值是装饰器。来看一下实现的代码。def make_header(level):print(Create decorator)# 这部分跟通常的装饰器一样只是wrapper通过闭包访问了变量leveldef decorator(func):print(Initialize)def wrapper():print(Call)return {1}.format(level, func())return wrapper# make_header返回装饰器return decorator看了实现代码装饰器的构造和调用的时序已经很清楚了。 make_header(2)... def get_content():... return hello world...Create decoratorInitialize get_content()Callhello world2. 如何装饰有参数的函数为了有条理地理解装饰器之前例子里的被装饰函数有意设计成无参的。我们来看个例子。make_bolddef get_login_tip(name):return Welcome back, {}.format(name)最直接的想法是把get_login_tip的参数透传下去。class make_bold(object):def __init__(self, func):self.func funcdef __call__(self, name):return {}.format(self.func(name))如果被装饰的函数参数是明确固定的这么写是没有问题的。但是make_bold明显不是这种场景。它既需要装饰没有参数的get_content又需要装饰有参数的get_login_tip。这时候就需要可变参数了。class make_bold(object):def __init__(self, func):self.func funcdef __call__(self, *args, **kwargs):return {}.format(self.func(*args, **kwargs))当装饰器不关心被装饰函数的参数或是被装饰函数的参数多种多样的时候可变参数非常合适。可变参数不属于装饰器的语法内容这里就不深入探讨了。3. 一个函数能否被多个装饰器装饰下面这么写合法吗make_italicmake_bolddef get_content():return hello world合法。上面的的代码和下面等价留意一下装饰的顺序。def get_content():return hello worldget_content make_bold(get_content) # 先装饰离函数定义近的get_content make_italic(get_content)4. functools.wraps有什么用Python的装饰器倍感贴心的地方是对调用方透明。调用方完全不知道也不需要知道调用的函数被装饰了。这样我们就能在调用方的代码完全不改动的前提下给函数patch功能。为了对调用方透明装饰器返回的对象要伪装成被装饰的函数。伪装得越像对调用方来说差异越小。有时光伪装函数名和参数是不够的因为Python的函数对象有一些元信息调用方可能读取了。为了连这些元信息也伪装上functools.wraps出场了。它能用于把被调用函数的__module____name____qualname____doc____annotations__赋值给装饰器返回的函数对象。import functoolsdef make_bold(func):functools.wraps(func)def wrapper(*args, **kwargs):return {}.format(func(*args, **kwargs))return wrapper对比一下效果。 make_bold... def get_content():... Return page content... return hello world# 不用functools.wraps的结果 get_content.__name__wrapper get_content.__doc__# 用functools.wraps的结果 get_content.__name__get_content get_content.__doc__Return page content实现装饰器时往往不知道调用方会怎么用所以养成好习惯加上functools.wraps吧。