函数嵌套
内部函数使用外部函数变量
外部函数返回内部函数
简单示例
def outer(num1):
def inner(num2):
result=num1+num2
print(result)
return inner
inner=outer(1)
inner(2)
#或者outer(1)(2)
外部函数调用返回的是内部函数,再执行
- 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
注意点:
- 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
def work(name):
def say(msg):
print(f'{name}说:{msg}')
return say
work('张三')('在吗')
work('李四')('我再')
'''
张三说:在吗
李四说:我再
'''
内部函数修改外部函数的变量
def outer(num1):
def inner(num2):
# 修改外部函数的变量nonlocal 而不是 global
nonlocal num1
num1=10
print(num1+num2)
return inner
outer(5)(5)
# 15
装饰器
装饰器就是一个闭包函数,而装饰器外部函数传入的是需要装饰的函数,并再装饰器的内部函数调用
作用 就是再不改变函数的同时对其进行功能上的扩展!
如函数只有发表功能此时加上登录后再执行此函数
def decorate(func):
print('装饰器开始执行')
def inner():
print('登录')
func()
return inner
def comment():
print('发表功能')
comment=decorate(comment)
comment()
'''
装饰器开始执行
登录
发表功能
'''
分析:
·comment=decorate(comment) comment()
装饰器函数传入需要装饰的comment 并放回内部函数 然后内部函数再去执行
comment=decorate(comment) 且只执行这句不执行comment()运行发现
装饰器开始执行 这句话执行了 此时comment就是inner函数 都执行需要再次调用inner()
语法糖简写
def decorate(func):
print('装饰器开始执行')
def inner():
print('登录')
func()
return inner
@decorate #此语法糖代替了下面注释的那句话
def comment():
print('发表功能')
# comment=decorate(comment)
comment()
'''
装饰器开始执行
登录
发表功能
'''
以上只有函数被标记装饰器语法糖,不写调用comment(),运行也会执行外部函数中的内容
计算下某函数的耗时
import time
def decorate(func):
def inner():
begin=time.time()
func()
end=time.time()
print(end-begin)
return inner
@decorate
def fun():
for i in range(10000):
print(i)
fun()
装饰带有参数的函数
def decorate(fn):
def inner(a,b):
fn(a,b)
return inner
@decorate
def sum(a,b):
print(a+b)
sum(5,3)
# 分析:
# 1.sum有参数,当该函数当作参数传进装饰器中,那么此时内部函数调用时也要带上该参数,
# 2.若该调用有参数,内部函数也要有该参数,此时调用sum(5,3)
装饰带有返回值的函数
def decorate(fn):
def inner(a,b):
return fn(a,b)
return inner
@decorate
def sum(a,b):
return a+b
print(sum(5,3))
装饰带有不定长参数的函数
tup = (1, 2, 3)
a, b, c = tup
print(a, b, c) # 1 2 3
dic = {'name': 'znd', 'age': 18}
d, e = dic
print(d, e) # name age
# 不定长参数使用元组和字典形式接收需要加上* **
def decorate(fn):
def inner(*args, **kwargs):
print('努力计算中...')
fn(*args, **kwargs)
return inner
# 对不定长参数的数进行累加计算
@decorate
def sum(*args, **kwargs):
result = 0
for v in args:
result += v
for v in kwargs.values():
result += v
print(result)
sum(1, 2, 3, a=10, b=5)
#努力计算中...
# 21
通用装饰器
# 添加输出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result
return inner
# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
return result
@logging
def subtraction(a, b):
result = a - b
print(result)
result = sum_num(1, 2, a=10)
print(result)
subtraction(4, 2)
运行结果:
--正在努力计算--
13
--正在努力计算--
2
总之函数结构什么样子的 内部函数就应该是什么样的!
多个装饰器的使用示例代码
def make_div(fn):
def inner():
return "<div>" + fn() + "</div>"
return inner
def make_p(fn):
def inner():
return '<p>' + fn() + '</p>'
return inner
@make_p
@make_div
def content():
return '人生苦短,我用python'
print(content())
#<p><div>人生苦短,我用python</div></p>
先执行最近一个装饰器,执行完毕后再执行其他的 按照顺序依次类推
跟@装饰写的顺序有关
带有参数的装饰器
装饰器参数只能传入函数,此时若想额外带有其他参数,就再此装饰器外层包裹一个函数,
此函数带有该参数,且该函数返回装饰器
# 装饰器外层的函数携带参数
def logging(flag):
# 装饰器
def decorate(fn):
def inner(*args, **kwargs):
if flag == '+':
print('正在努力加法计算')
elif flag == '-':
print('正在努力减法计算')
return fn(*args, **kwargs)
return inner
# 返回装饰器
return decorate
@logging('+')
def add(a, b):
return a + b
@logging('-')
def sub(a, b):
return a - b
print(add(4, 5))
print(sub(5, 1))
'''
正在努力加法计算
9
正在努力减法计算
4
'''
类装饰器
class Check:
def __init__(self,fn):
self.__fn=fn
def __call__(self, *args, **kwargs):
print('增加的功能')
self.__fn()
@Check
def comment():
print('发表评论')
comment()
# comment=Check(comment)
# comment()
# 增加的功能
# 发表评论
分析:
之前是函数装饰器的时候,当@装饰器的时候就会自动调用该装饰器,
类自动调用时机就是init函数,此时将需要被装饰的函数传进去,
另外类想函数一样 函数()形式直接调用,需要用到call方法。
此时需要再call方法中调用fn()
- @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
- 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
- 在call方法里进行对fn函数的装饰,可以添加额外的功能。