一:闭包的定义:
目的:保证变量不会因为外部函数调用而销毁。
1:在函数嵌套的前提下,内部函数使用了外部函数的变量,外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
2:闭包的形成条件:
函数嵌套 //内部函数使用了外部函数的变量 // 外部函数返回了内部函数对象。
3:简单闭包:
def func_out(num1):
def func_inner(num2):
result = num1 + num2;
print("结果是:",result)
return func_inner
f = func_out(1)
f(2)
f(3)
4:闭包的作用:闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
5:为什么不会随着外部函数的调用而销毁?
答:变量当自己的引用计数等于0的时候就会被销毁。当将1赋值给num1的时候num1的引用计数是1,然后定义fun_inner,由于fun_inner里面用到了num1(result = num1 + num2;),所以num1的引用计数变成了2,最后函数调用完成时,销毁所有变量(本质是引用变量减一),所以num1引用计数又变成了1。所以外层函数调用完,变量是不会销毁的。
6:修改闭包内使用的外部变量。nonlocal关键字:
nonlocal是使用外层函数内的变量,如果不写,实际上是内层定义的自己新的变量与外层的变量是无关的。
# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
nonlocal num1 # 告诉解释器,此处使用的是 外部变量a
# 修改外部变量num1
num1 = 10
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
print(num1)
func_inner(1)
print(num1)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
二:装饰器:特殊的闭包。:区别是只有一个变量,并且变量是函数类型。
1:定义:给已有函数增加额外功能的函数。
2:功能特点:
不修改已有函数的源代码
不修改已有函数的调用方式
给已有函数增加额外的功能
3:简单代码:
def check(fn):
def inner():
print("请先登录")
fn()
return inner
def comment():
print("发表评论")
comment = check(comment)
comment()
分析:开始comment 指向本函数,check(comment)让fn和comment 同时指向本函数,当check返回inner的时候,comment重新指向修饰函数的内层函数。此时(comment指向修饰函数内层函数,fn指向了本函数)。当我们调用comment()函数的时候是执行修饰函数的内层函数,而内层函数又可以调用本函数,所以上实现了在本函数上扩展功能,但是不修改本函数。
4:语法糖写法:@修饰函数
# 添加一个登录验证的功能
def check(fn):
print("装饰器函数执行了")
def inner():
print("请先登录....")
fn()
return inner
# 使用语法糖方式来装饰函数
@check
def comment():
print("发表评论")
comment()
5:利用修饰器计算函数的执行时间:
import time
def out_hello(fn):
def inner_hello():
before = time.time()
fn()
after = time.time()
print("函数所用时间是:", after-before)
return inner_hello
@out_hello
def print_hello():
for i in range(100000):
print("hello:%d" % i)
ph = print_hello()
6:装饰器能不能携带参数?可以返回值吗?
def out_count(fn):
def inner_count(num1,num2):
print("正在做加法其他的功能。。。")
sum = fn(num1,num2)
print("做完加法做其他的。。。。。")
return sum
return inner_count
@out_count
def count_num(num1,num2):
sum = num1 + num2
print("num1 + num2=",sum)
return sum
sum = count_num(1,2)
print("最后结果: %d" % sum)
分析装饰过程:@out_count 本质是:count_num = out_count(count_num), 首先开始的时候,count_num指向本函数,传参过去后,fn和count_num同时指向本函数,当装饰函数返回inner_count的时候,count_num重新指向装饰内层函数。
调用过程:count_num(1,2),先调用装饰内层函数,将1,2传递给inner_count(num1,num2) ,当执行到 sum = fn(num1,num2)的时候,inner_count(num1,num2) 中的num1 和num2赋值给fn(num1,num2)中的num1和num2,而fn此时指向本函数,所以又将fn中的num1,num2传递给count_num(num1,num2)。当count_num执行完后将sum返回给装饰器函数内层函数中的sum,最后这个内层函数又把自己的sum,返回给主程序数中的sum。
注意:由于传参先传给装饰器内层函数,然后内存函数调用外层传进来的函数,最后调用本函数。所以参数个数,内层函数,内层中的外层传入的函数,都要和本函数保持一致。
7:装饰器可以携带不定长参数吗?
def loging(fn):
def inner(*args,**kwargs):
print("----输出执行函数前的日志信息")
# 注意这里是用到拆包技术,这里是把inner(参数中的)变成1,2,3,x=100,y=200这种格式
sum = fn(*args,**kwargs)
print("-----输出函数执行后的日志信息")
return sum
return inner
@loging
def sum_num(*args ,**kwargs):
sum = 0
for i in args:
sum = sum + i
for i in kwargs.values():
sum = sum + i
return sum
sum = sum_num(1,2,3,x=100,y=200)
print("求和之后的结果是:", sum)
8:多个装饰器如何执行流程???
def add_div(fn2):
def inner_div():
return "<div>" + fn2() + "</div>"
return inner_div
def add_p(fn1):
def inner_p():
return "<p>"+fn1()+ "</p>"
return inner_p
@add_div
@add_p
def content():
return "人生苦短,我用python!"
main_content = content()
print(main_content)
输出结果:
<div><p>人生苦短,我用python!</p></div>
分析:装饰过程:
**@add_p 相当于:content = add_p(content),开始时,content指向本函数,当调用add_p(content) 后,fn1也指向本函数,然后add_p返回给content自己的内层函数,所以此时content指向第一个修饰器的内层函数。@add_div 相当于 content = add_div(content),此时开始content 指向第一个修饰器的内层函数,通过传参将第一个修饰器的内层函数传递给fn2,然后add_div把自己的内层函数返回给content,所以content指向第二个修饰器的内层函数。**
总结:最终content指向第二个修饰器的内层函数,fn2指向第一个修饰器的内层函数,fn1指向本函数 content()。
调用过程:调用过程:content()实际上等于inner_div(),所以先调用inner_div,然后执行 fn2(),实际上是inner_p(),然后执行fn1实际上是content()函数。然后从content函数开始向上返回,content向上返回给inner_p:"人生苦短,我用python!",inner_p组合后向上返回给inner_div:<p>人生苦短,我用python!</p>,然后inner_div向上返回给主程序<div><p>人生苦短,我用python!</p></div>
9:带有参数的装饰器:
def logging(flag):
def out_num(fn):
def inner_num(x,y):
if flag == "+":
print("这是加法操作的准备工作")
sum2 = fn(x,y)
return sum2
elif flag == "-":
print("这是减法操作的准备工作")
mul2 = fn(x,y)
return mul2
return inner_num
return out_num
@logging("+")
def add_num(a,b):
sum = a+b
return sum
@logging("-")
def mul_num(a,b):
mul = a-b
return mul
sum = add_num(1,2)
print(sum)
mul = mul_num(2,1)
print(mul)
分析:@logging("+"):可以拆分成两部分:logging("+")传递给装饰器外层的函数,返回一个装饰器类型的对象out_num 然后@和out_num 又拼接成语法糖。
问题:为什么在修饰器外面增加一个函数,内部就能访问传入参数呢?
由于这个外层函数传入变量,返回内层函数,内层函数使用外层函数变量,所以修饰器实际上是一个闭包了,而披上的是外层函数,闭包内可以访问外层函数变量。
10:类装饰器:
class Check(object):
def __init__(self ,fn):
self.__fn = fn;
def __call__(self, *args, **kwargs):
print("发表评论前的准备工作。。。")
self.__fn()
@Check
def comment():
print("发表评论")
comment()
@Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
在call方法里进行对fn函数的装饰,可以添加额外的功能。
浙公网安备 33010602011771号