不管是闭包函数还是装饰器,都是基于函数的。
我们知道,函数名其实就是指向一段内存空间的地址,既然是地址,那么我们可以利用这种特性。
满足以下三点中的两点即称为高级函数:
1,函数名可以作为一个值
函数名可以作为一个值即可以将一个函数名赋值给一个变量,此时该变量拥有和函数同样的功能,如下所示:
In: abs(-1)
Out: 1
In: a = abs
In: a(-1)
Out: 1
2,函数名可以作为返回值
在一个函数中可以定义另外一个函数,可以将定义的那个函数返回。
3,函数名可以作为一个参数
可以将函数名作为一个函数传入另外一个函数。
闭包函数:
闭包函数必须满足两个条件:
1.函数内部定义的函数 ;
2.包含对外部作用域而非全局作用域的引用。
下面通过一些实例来说明闭包函数:
实例一:以下仅仅在函数内部定义了一个函数,但并非闭包函数.
def outer():
def inner():
print("inner func excuted")
inner() # 调用执行inner()函数
print("outer func excuted")
outer() # 调用执行outer函数
####输出结果为##########
inner func excuted
outer func excuted
实例二:以下在函数内部定义了一个函数,而且还引用了一个外部变量x,那么这个是闭包函数么?答案:不是
x = 1
def outer():
def inner():
print("x=%s" %x) # 引用了一个非inner函数内部的变量
print("inner func excuted")
inner() # 执行inner函数
print("outer func excuted")
outer()
#####输出结果########
x=1
inner func excuted
outer func excuted
这里的变量x,是属于全局变量,而非外部作用于域的变量。再来看看下面例子:
def outer():
x = 1
def inner():
print("x=%s" %x)
print("inner func excuted")
inner()
print("outer func excuted")
outer()
#####输出结果#########
x=1
inner func excuted
outer func excuted
显然,上面实例满足闭包函数的条件。作为一个闭包函数,必须得满足上述的两个条,缺一不可。但是,一般情况下,我们都会给闭包函数返回一个值。这里先不说为什么。在接下来的内容中,你会看到这个返回值的用途。
def outer():
x = 1
def inner():
print("x=%s" %x)
print("inner func excuted")
print("outer func excuted")
return inner # 返回内部函数名
outer()
现在我们来抽象的定义一下闭包函数。它是函数和与其相关的引用环境组合而成的实体。在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起成为闭包。在上面实例中,我们可以发现,闭包函数,它必须包含自己的函数以及一个外部变量才能真正称得上是一个闭包函数。如果没有一个外部变量与其绑定,那么这个函数不能算得上是闭包函数。
那么怎么知道一个闭包函数有多少个外部引用变量呢?看看下面代码.
def outer():
x = 1
y = 2
def inner():
print("x= %s" %x)
print("y= %s" %y)
print(inner.__closure__)
return inner
outer()
######输出结果#######
(<cell at 0x0000018DE9050EB8: int object at 0x00000000652660A0>, <cell at 0x0000018DE90509A8: int object at 0x00000000652660C0>)
结果表明,在inner内部,引用了两个外部局部变量。如果引用的是非局部变量,那么这里输出的为None。
闭包函数的特点:1.自带作用域 2.延迟计算
那么闭包函数有什么作用呢?我们清楚的知道,闭包函数在定义时,一定会绑定一个外部环境。这个整体才能算的上是一个闭包函数,那么我们可以利用这个绑定特性,来完成某些特殊的功能。
装饰器
有了以上基础,对于装饰器就好理解了.
装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。
特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码
(1)无参装饰器
有如下实例,我们需要计算一下代码执行的时间。
import time, random
def index():
time.sleep(random.randrange(1, 5))
print("welcome to index page")
根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.
import time, random
def outer(func): # 将index的地址传递给func
def inner():
start_time = time.time()
func() # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址
def index():
time.sleep(random.randrange(1, 5))
print("welcome to index page")
index = outer(index) # 这里返回的是inner的地址,并重新赋值给index
index()
#装饰器实现计时
但是,有些情况,被装饰的函数需要传递参数进去,有些函数又不需要参数,那么如何来处理这种变参数函数呢?下面来看看有参数装饰器的使用情况.
(2)有参装饰器
def outer(func): # 将index的地址传递给func
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs) # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址
下面来说说一些其他情况的实例。
如果被装饰的函数有返回值
def timmer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs) #res来接收home函数的返回值
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
def home(name):
time.sleep(random.randrange(1,3))
print('welecome to %s HOME page' %name)
return 123123123123123123123123123123123123123123
这里补充一点,加入我们要执行被装饰后的函数,那么应该是如下调用方式:
home = timmer(home) # 等式右边返回的是wrapper的内存地址,再将其赋值给home,这里的home不在是原来的的那个函数,而是被装饰以后的函数了。
像home = timmer(home)这样的写法,python给我们提供了一个便捷的方式—–-语法糖@.
以后我们再要在被装饰的函数之前写上@timmer,它的效果就和home = timmer(home)是一样的。
如果一个函数被多个装饰器装饰,那么执行顺序是怎样的?
import time
import random
def timmer(func):
def wrapper():
start_time = time.time()
func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return wrapper
def auth(func):
def deco():
name=input('name: ')
password=input('password: ')
if name == 'egon' and password == '123':
print('login successful')
func() #wrapper()
else:
print('login err')
return deco
@auth # index = auth(timmer(index))
@timmer # index = timmer(index)
def index():
time.sleep(3)
print('welecome to index page')
index()
实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。
关于装饰器,还有一些高级用法,有兴趣的可以自己研究研究。
(3)类装饰器
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print('class decorator runing')
self._func()
print('class decorator ending')
@Foo # bar = Foo(bar)
def bar():
print('bar')
bar() # Foo(bar)()
# 结果
# class decorator runing
# bar
# class decorator ending
class Foo(object):
def __init__(self):
pass
def __call__(self, func):
def _call(*args, **kw):
print('class decorator runing')
return func(*args, **kw)
return _call
class Bar(object):
@Foo()
def bar(self, test, ids): # bar = Foo()(bar)
print('bar')
Bar().bar('aa', 'ids')
转自:https://www.cnblogs.com/huchong/p/7725564.html,省略了一些内容
参考:
1. https://blog.csdn.net/xiangxianghehe/article/details/77170585
2. https://blog.csdn.net/lantian_123/article/details/78094145
3. http://python.jobbole.com/82344/