inner()
outer() #输出结果 2
如果我们在外函数里不直接调用内函数,而是通过return inner返回一个内函数的引用 这时会发生什么呢? 你将会得到一个内函数对象,而不是运行结果。
def outer():
x = 1
def inner():
y = x + 1
print(y)
return inner
outer() # 输出<function outer..inner at 0x039248E8>
f1 = outer()
f1() # 输出2
上述这个案例比较简单,因为outer和inner函数都是没有参数的。我们现在对上述代码做点改动,加入参数。你可以看到外函数的参数或变量可以很容易传递到内函数。
def outer(x):
a = x
def inner(y):
b = y
print(a+b)
return inner
f1 = outer(1) # 返回inner函数对象
f1(10) # 相当于inner(10)。输出11
如果上例中外函数的变量x换成被装饰函数对象(func),内函数的变量y换成被装饰函数的参数,我们就可以得到一个通用的装饰器啦(如下所示)。你注意到了吗? 我们在没对func本身做任何修改的情况下,添加了其它功能, 从而实现了对函数的装饰。
def decorator(func):
def inner(*args, **kwargs):
add_other_actions()
return func(*args, **kwargs)
return inner
##### 闭包
[专题四:python之作用域和闭包详解]( )
#### 面试中的装饰器
##### 通用的装饰器
面试官往往会给你提一些简单的需求,如返回被装饰函数执行时间,函数的名称等,这时候要学会灵活的转变。
def hint(func):
def wrapper(*args, **kwargs):
print(‘{} is running’.format(func.name))
return func(*args, **kwargs)
return wrapper
@hint
def hello():
print(“Hello!”)
>
> **执行结果**
> **调用:** hello()
> hello is running.
> Hello!
> 值得一提的是被装饰器装饰过的函数看上去名字没变,其实已经变了。当你运行**print(hello.**name**)** 后,你会发现它的名字已经悄悄变成了wrapper,这显然不是我们想要的。这一点也不奇怪,因为外函数返回的是由wrapper函数和其外部引用变量组成的闭包。
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201211095412663.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NTgxOTYx,size_16,color_FFFFFF,t_70)
> 为了解决这个问题保证装饰过的函数\_\_name\_\_属性不变,我们可以使用functools模块里的wraps方法,先对func变量进行wraps。下面这段代码可以作为编写一个通用装饰器的示范代码,注意收藏哦。
>
>
>
from functools import wraps
def hint(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(‘{} is running’.format(func.name))
return func(*args, **kwargs)
return wrapper
@hint
def hello():
print(“Hello!”)
如果你非常的熟悉上面的这些内容,你就可以轻松的写一个通用的装饰器。
##### 带参的高级装饰器
前面几个装饰器一般是内外两层嵌套函数。如果我们需要编写的装饰器本身是带参数的,我们需要编写三层的嵌套函数,其中最外一层用来传递装饰器的参数。现在我们要对@hint装饰器做点改进,使其能通过@hint(coder=“LeooO”)传递参数。该装饰器在函数运行前给出提示的时候还显示函数编写人员的名字。完整代码如下所示:
from functools import wraps
def hint(coder):
def wrapper(func):
@wraps(func)
def inner_wrapper(*args, **kwargs):
print(‘{} is running’.format(func.name))
print(‘Coder: {}’.format(coder))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@hint(coder=“LeooO”)
def hello():
print(“Hello!”)
##### 多个装饰器
先看一段代码
def d1(f):
def inner1(*args, **kwargs):
print(1)
f()
print(“我是外边的装饰器”)
return inner1
def d2(f):
def inner2(*args, **kwargs):
print(2)
print(f.name)
f()
print(“我是里边的装饰器”)
return inner2
@d1
@d2
def func():
print(‘哈哈哈’)
func()
执行结果是啥呢?
>
> 2
> 1
> func
> 哈哈哈
> 我是里边的装饰器
> 我是外边的装饰器
>
>
>
这是咋回事呢?我换种写法你思考一下。
def d1(f):
def inner1(*args, **kwargs):
print(2)
f()
print(“我是外边的装饰器”)
return inner1
def d2(f):
def inner2(*args, **kwargs):
print(1)
print(f.name)
f()
print(“我是里边的装饰器”)
return inner2
def func():
print(‘哈哈哈’)
res = d1(d2(func))
如果你还是不明白,没有关系,再来一段代码
from functools import wraps
def A(funE_decorated_by_C):
@wraps(funE_decorated_by_C)
def redecorated_E(str):
print(“开始执行”, end=“——>”)
return funE_decorated_by_C(str) + ’ > redecorated by A’
return redecorated_E
def C(funE):
@wraps(funE)
def decorated_E(str):
return funE(str) + ’ > decorated by C’
return decorated_E
@A
@C
def E(str):
return str
print(E('A string is '))
print(E.name)
**#开始执行——>A string is > decorated by C > redecorated by A**
**难理解的地方在C和A在装饰过程中执行的调用关系**
* **可以看到,先调用那个装饰器,那个装饰器就先运行**
print(“开始执行”, end="——>")
* **继续执行 A装饰的是C(A) 即funE\_decorated\_by\_C**
调用 funE\_decorated\_by\_C 执行C
* **C执行 调用E函数 返回的结果作为A funE\_decorated\_by\_C的参数**
* **最终在A中调用E 返回 A**
**案例代码**
def decorator_b(fun):
def inner_b(*args, **kwargs):
print(‘This is inner_b’)
print(“*****”)
# print(fun(*args, **kwargs))
return fun(*args, **kwargs) + 2
return inner_b
def decorator_a(fun):
def inner_a(*args, **kwargs):
print(‘This is inner_a’)
return fun(*args, **kwargs) + 11
最后
🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!