一、闭包
- 什么是闭包?在了解这个概念之前,我们先来看一个小例子。
def create_line(k,b):
'''定义一个表示直线的函数'''
def line(x):
ret = k * x + b
return ret
return line
line_11_22 = create_line(11,22) #返回的是一个函数line
print(line_11_22(1)) #line_11_22(1)相当于执行line(1)
print(line_11_22(2))
print(line_11_22(3))
上面的例子中,我们看到有一个外部函数,内部定义了一个函数,外部函数返回的是内部函数,而内部函数同时使用到了外部函数的变量k和b。
像这样,在一个外部函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。在上面的例子中,我们发现,当我们在执行输出的时候,传入的是一个参数,但是却可以计算值来,从这点可以看出,闭包可以保存之前出入的数据。
-
特点:闭包可以保存数据和功能。
-
通过闭包,我们可以修改外部函数中的变量,在Python3中我们修改外部变量的值是通过使用关键字nonlocal来实现的,如下:
def set_func(count=0):
def counter():
nonlocal count # nonlocal 表示引用的是外部函数的变量。
count += 10
return count
return counter
ct = set_func(10)
print(ct()) # 20
二、装饰器
装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以下面跟大家介绍下装饰器,在介绍装饰器之前,先来一个小例子。
def task1():
print('无参数')
task1()
如上面的例子,如果我们要修改task1函数的功能必须每次都修改task1函数内部的代码,这样做很麻烦,我们其实可以用装饰器来解决这个问题。那么什么是装饰器呢?
- 装饰器:在不修改原有函数定义的前提下,给函数扩展相应的功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。我们使用:@装饰器函数名 放在一个函数定义前面来装饰一个函数。
那么装饰器可以针对哪些函数呢,下面我们一一列举:
1. 无参数无返回值的函数
def decorater(func):
def wrapper():
func()
return wrapper
@decorater
def task1():
print('无参数')
task1()
分析:
当我们在task1函数定义前加了@decorater之后,装饰器函数底层做了一个操作,即:task1 = decorater(task1),此时func指向了task1,task1指向了wrapper,当我们在调用task1()的时候,相当于执行了wrapper(),在wrapper函数里面执行了task1函数。
2. 无参数有返回值的函数
def decorater(func):
def wrapper():
return func()
return wrapper
@decorater
def task2():
return '有返回值'
t = task2()
print(t)
3. 有参数无返回值的函数
def decorater(func):
def wrapper(num):
func(num)
return wrapper
@decorater
def task3(num):
print('有参数,无返回值 %d' % num)
task3(10)
4. 有参数有返回值的函数
def decorater(func):
def wrapper(num):
return func(num)
return wrapper
@decorater
def task4(num):
return num + 10
t2 = task4(10)
print(t2)
5. 万能装饰器
上面的4种方法都没什么问题,但是写起来太麻烦,下面我们实现一种万能的装饰器,可以有、无参数,也可以有、无返回值。
def decorater(func):
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
@decorater
def task4(*args,**kwargs):
print('args:',args)
print('kwargs:',kwargs)
return 10
t1 = task4()
print(t1)
t2 = task4(10,20)
print(t2)
t3 = task4(15,25,age=30)
print(t3)
运行结果:
args: ()
kwargs: {}
10
args: (10, 20)
kwargs: {}
10
args: (15, 25)
kwargs: {'age': 30}
10
可以看到,无论我们装饰的函数有没有参数和返回值,都可以装饰成功。
6. 多个装饰器装饰一个函数
#短信验证的装饰器
4 def message_fun(func):
5 print('正在进行短信验证装饰')
6 def call_mesage():
7 print('正在进行短信安全验证')
8 func()
9 return call_mesage
#手势验证的装饰器
12 def gesture_fun(func):
13 print('正在进行手势验证装饰')
14 def call_gesture():
15 print('正在进行手势安全验证')
16 func()
17 return call_gesture
19 @message_fun # t = message_fun(t)
20 @gesture_fun # t = gesture_fun(t)
21 def t():
22 print('装饰结束了哟')
24 t()
运行结果:
正在进行手势验证装饰
正在进行短信验证装饰
正在进行短信安全验证
正在进行手势安全验证
装饰结束了哟
从运行结果我们可以看到,装饰的时候是从内到外,执行的时候是从外到内,这是什么意思呢?就是谁距离被装饰的函数最近,谁最先装饰这个函数,谁距离被装饰的函数越远,谁最先执行。
我们来分析一下这个过程(-》表示指向):
分析装饰过程:
4行:定义函数message_fun
12行:定义函数gesture_fun
19行:此时t还没有定义,所以等待执行
20行:此时t也还没定义,所以也是等待执行
21行:定义函数t
20行:
1. 执行函数gesture_fun(t),跳到第13行,执行 此时 gesture_fun.func -》 t
2. print('正在进行手势验证装饰')
3. 第14行,定义函数call_gesture ,t = gesture_fun(t), 此时 t -》call_gesture
19行:
1.执行函数message_fun(t),跳到第5行,执行 此时 message_fun.func -》call_gesture
2.print('正在进行短信验证装饰')
3.第6行,定义函数call_mesage,t = message_fun(t),此时 t -》 call_mesage
24行:
1. 此时t - 》 call_mesage,执行第7行
2. print('正在进行短信安全验证'),在执行第8行,执行func(),跳到第15行,
3. print('正在进行手势安全验证'),执行func(),跳到第21行,
4. print('装饰结束了哟')
7. 类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的。
class Person(object):
'''类装饰器'''
def __init__(self,func):
self.__func = func
def __call__(self, *args, **kwargs):
self.__func()
@Person #等价于 task_test = Person(task_test)
def task_test():
print('hehe')
task_test() #执行这个对象
8. 装饰器带参数,在原有的基础上设置外部变量
import functools
86 def timefun_arg(pre='hello'):
87 def timefun(func):
88 @functools.wraps(func)
89 def wrapped_func():
90 print('%s called at %s :%s' % (func.__name__,ctime(), pre))
91 return func()
92 return wrapped_func
93 return timefun
95 @timefun_arg('Hi') #底层实现:foo = timefun_arg('Hi')(foo)
96 def foo():
97 print('哈哈')
99 foo()
我们分析下上面的过程:
分析过程:
86行:定义函数timefun_arg
95行:先执行 timefun_arg('Hi'),此时pre='Hi',然后timefun -》timefun_arg('Hi'),此时foo还未定义,所以等待。。
96行:定义foo函数
95行:此时执行 timefun(foo),然后func -》foo,返回wrapped_func,此时foo -》wrapped_func
99行:foo() 相当于执行wrapped_func(),然后执行 print('%s called at %s :%s' % (func.__name__,ctime(), pre))
91行:返回func(), 此时func —》foo,所以跳到第97行,打印:print('哈哈')
以上的decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'foo'变了'wrapped_func',因为返回的那个wrapped_func()函数名字就是'wrapped_func',所以,需要把原始函数的__name__等属性复制到wrapped_func()函数中,否则,有些依赖函数签名的代码执行就会出错。不需要编写wrapped_func.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的。上面的一个例子我们已经加了这段代码。在打印的时候发现,他的名字已经变成了跟函数名字一样的。
最后我们再来实现一个需求:
'''
思考一下能否写出一个@log的decorator,使它既支持:
@log # f = log(f)
def f():
pass
又支持:
@log('execute') #f = log('execute')(f)
def f():
pass
'''
代码如下:
113 def log(arg='haha'):
114 def decorator(func):
115 @functools.wraps(func)
116 def wrapper():
117 print('name: %s,arg: %s' % (func.__name__,arg))
118 return func()
119 return wrapper
120 if callable(arg): #说明使用的是 @log方式
121 return decorator(arg)
122 else:
123 return decorator
124
125 @log
126 def f1():
127 print('大家')
129 @log('哈哈')
130 def f2():
131 print('笑了')
133 f1()
134 f2()
我们也来分析一下过程:
分析过程:
113行:定义函数log
125行:底层实现:f1 = log(f1),arg—>f1,
114行,定义一个函数decorator,
120行,满足条件,直接返回decorator(arg),此时f1-》decorator(arg),执行114行,此时func-》arg,返回wrapper,
最终f1-》wrapper
133行,执行f1() 相当于执行wrapper(),print('name: %s,arg: %s' % (func.__name__,arg)),
返回func(),此时func-》arg-》f1,相当于执行f1(),打印 print('大家')
129行,底层实现:f2 = log('哈哈')(f2),先执行log('哈哈'),arg->'哈哈',114行,定义函数decorator,
120行,不满足,123行,满足,返回,此时f2 = decorator(f2),此时func-》f2,返回wrapper,最终f2 -》wrapper
134行,f2(),相当于执行wrapper(),打印,print('name: %s,arg: %s' % (func.__name__,arg)),
返回func(),此时func-》f2,所以执行f2(),130行,打印print('笑了')
网友评论