闭包的定义
将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包
我们知道函数在Python中是第一类对象,也就是说可以把它们当作参数传递给其他函数,放在数据结构中,以及作为函数的返回结果。
我们定义一个函数,它接受另一函数作为输入并调用它
foo.py
def call(func):
return func
我们在另一个程序中去调用这个函数
cal_close.py
from python_book import foo
def helloworld():
return 'hello world'
print(foo.call(helloworld))
我们把函数当作数据处理时,它将隐式地携带与定义该函数的周围环境相关的信息,这将影响到函数中自由变量的绑定方式。
我们在刚才的栗子中加入一个变量的定义
foo.py
x = 42
def call(func):
return func
然后在调用函数的地方同样也加入自己的函数定义
cal_close.py
from python_book import foo
x = 37
def helloworld():
return 'hello world is %d' % x
print(foo.call(helloworld))
>>> hello world is 37
在这个栗子中,函数helloworld()使用的x的值是在与他相同的环境中定义的,即使在foo.py中也定义了一个变量x,而且这里也是实际调用helloworld()函数的地方,但x的值与helloworld()函数执行时使用的x不同
事实上所有函数都拥有一个指向了定义该函数的全局命名空间的globals属性,始终对应于定义函数的闭包模块,闭包将捕捉内部函数执行所需的整个环境
我们可以看一个闭包函数的栗子
def countdown(n):
def next():
nonlocal n
r = n
n -= 1
return r
return next
# 使用
next = countdown(10)
while True:
v = next()
if not v: break
print(v)
在Python中,装饰器是很重要的高级函数。要掌握装饰器我们必须掌握相关的知识
函数作用域
LEGB原则
函数即对象
在Python中,函数和我们之前的数据类型等都一样都是对象,而且函数是最高级的对象
在内存中和对象一样,也是将函数的指针存储在函数名中
既然函数和对象相同那么函数自然满足对象的几个条件
1.可以被赋值给其他变量
def foo():
print('foo')
bar=foo
bar()
foo()
print(id(foo),id(bar)) #4321123592 4321123592
函数的函数名其实就是函数的指针,可以将函数的函数名赋值给其他的变量
2.其可以被定义在另外一个函数内(作为参数&作为返回值):
#*******函数名作为参数**********
def foo(func):
print('foo')
func()
def bar():
print('bar')
foo(bar)
#*******函数名作为返回值*********
def foo():
print('foo')
return bar
def bar():
print('bar')
b=foo()
b()
函数的嵌套及闭包
Python允许创建嵌套函数,通过在函数内部使用def关键字声明一个函数作为内部函数
看下面一个例子:
#想执行inner函数,两种方法
def outer():
x = 1
def inner():
print (x) # 1
# inner() # 2
return inner
# outer()
in_func=outer()
in_func()
作为调用内部函数inner,有下面两种方法
in_func=outer()
in_func()
###########
inner()(已经加载到内存啦)
如果直接在外部调用内部函数inner就会报错,原因就是这里找不到这个引用变量
但是这里就会有一个问题,在内部函数被调用执行的时候,它的外部函数也就是outer()已经执行完毕了,那么为什么inner还是可以调用声明在外部outer函数中的变量x呢?
这里就涉及到我们说的闭包的概念,因为outer里return的inner是一个闭包函数,所以就会有这个x变量
我们也就可以抛出闭包的定义
如果在一个内部函数中,对在外部作用域(且不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包,内部函数可以称为闭包函数
在上面的例子中,inner就是内部函数,inner中引用了外部作用域的变量x(x在外部作用域outer,不在模块的全局作用域)。
所以这个inner函数就是闭包函数
如下函数,内部闭包函数的作用是给外部的函数增加字符串参数
>>> def saying(something):
... def outsaying():
... return "is inner read params %s" %something
... return outsaying
...
>>> a = saying('hello')
>>> a()
'is inner read params hello'
上面这个函数中 outsaying作为内部函数直接访问外部的变量所以是一个闭包函数,outsaying()函数可以得到参数something的值并且记录下来,return outsaying 这一行返回的是outsaying函数的一个复制(并没有直接调用)
装饰器概念
装饰器本质上来说是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也会一个函数对象。装饰器可以被用于:插入日志、性能测试、事物处理、缓存、权限校验等应用场景,我们有了装饰器我们就可以抽离大量与函数功能本身无关的雷同代码并继续重用。装饰器的作用就是为已经存在的对象添加额外的功能。
装饰器定义
装饰器是一个函数,只要用途是包装另一个函数或类。语法上使用特殊符号@表示装饰器
使用装饰器时,它们必须出现在函数或者类定义之前的单独行上,可以同时使用多个装饰器
···
@foo
@bar
@spam
def grok(x)
pass
···
上述代码等同于
def grok(x):
pass
grok = foo(bar(spam(grok)))
装饰器也可以接受参数
@eventhandler('a')
def handle_a(msg):
pass
@eventhandler('b')
def handle_b(msg):
pass
如果装饰器提供参数,装饰器的语义如下所示:
def handle_button(msg):
tmp = eventhandle('a')# 使用提供的参数调用装饰器
handle_button = tmp(handle_button) # 调用装饰器返回的函数
@trace
def square(x):
return x*x
等价:
def square(x):
return x*x
square = trace(square)
现在在生产中有这样的函数
def foo():
print('hello foo')
foo()
现在有一个新的需求,希望可以记录下函数的执行时间,在这种需求下我们最好不要修改源代码来实现这个功能,我们可以引入一个内置函数,而且为了不影响结构,我们还不应该改变函数的调用方式
这里我们可以引入一个内置函数
import time
def show_time(func):
def wrapper():
start_time=time.time()
func()
end_time=time.time()
print('spend %s'%(end_time-start_time))
return wrapper
def foo():
print('hello foo')
time.sleep(3)
foo=show_time(foo)
foo()
在上面这个例子中函数show_time就是装饰器。它将真正的业务方法func包裹在函数里面。
@符号是装饰器的语法糖,在定义函数的时候使用,避免多次的赋值操作
import time
def show_time(func):
def wrapper():
start_time=time.time()
func()
end_time=time.time()
print('spend %s'%(end_time-start_time))
return wrapper
@show_time #foo=show_time(foo)
def foo():
print('hello foo')
time.sleep(3)
@show_time #bar=show_time(bar)
def bar():
print('in the bar')
time.sleep(2)
foo()
print('***********')
bar()
这里:foo=show_time(foo)其实就是把内置函数wrapper引用的对象引用给了foo,而wrapper里的变量func之所以可以使用,就是因为wrapper是一个闭包函数
带参数的被装饰函数
import time
def show_time(func):
def wrapper(a,b):
start_time=time.time()
func(a,b)
end_time=time.time()
print('spend %s'%(end_time-start_time))
return wrapper
@show_time #add=show_time(add)
def add(a,b):
time.sleep(1)
print(a+b)
add(2,4)
- 不定长参数
#***********************************不定长参数
import time
def show_time(func):
def wrapper(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs)
end_time=time.time()
print('spend %s'%(end_time-start_time))
return wrapper
@show_time #add=show_time(add)
def add(*args,**kwargs):
time.sleep(1)
sum=0
for i in args:
sum+=i
print(sum)
add(2,4,8,9)
装饰器带参数
import time
def time_logger(flag=0):
def show_time(func):
def wrapper(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs)
end_time=time.time()
print('spend %s'%(end_time-start_time))
if flag:
print('将这个操作的时间记录到日志中')
return wrapper
return show_time
@time_logger(3)
def add(*args,**kwargs):
time.sleep(1)
sum=0
for i in args:
sum+=i
print(sum)
add(2,7,5)
@time_logger(3) 做了两件事:
- (1)time_logger(3):得到闭包函数show_time,里面保存环境变量flag
- (2)@show_time :add=show_time(add)
多层装饰器
def makebold(fn):
def wrapper():
return "<b>" + fn() + "</b>"
return wrapper
def makeitalic(fn):
def wrapper():
return "<i>" + fn() + "</i>"
return wrapper
@makebold
@makeitalic
def hello():
return "hello alvin"
hello()
匿名函数: lambda()函数
Python中,lambda函数是用一个语句表达的匿名函数,可以用它来代替小的函数。
# 定义一个函数,参数接收一个列表,并遍历
>>> def edit_story(words,func):
... for word in words:
... print(func(word))
...
>>> stairs = ['a','ab','abc','abcd']
>>> def add_str(word):
... return word+'!!!'
...
>>> edit_story(stairs,add_str)
a!!!
ab!!!
abc!!!
abcd!!!