函数的闭包与装饰器
一、函数对象
python一切皆对象,所以函数也是对象,也可以当做数据被处理
- 函数可以被引用
- 可以当作参数传递
- 返回值可以是函数
- 可以当作容器类型的元素
二、函数闭包
Python 支持函数式编程,所以存在闭包,闭包是由函数及其相关的引⽤环境组合⽽成的实体 , ⼀句话: 闭包 = 函数+引⽤环境,函数式编程中,当内嵌函数体内引⽤到体外的变量 时, 将会连同这些变量(引⽤环境)和内嵌函数体, ⼀块打包成⼀个整体返回。
如果在一个函数的内部定义了另一个函数,外部的我们叫它为外函数,内部的我们叫它内函数,那么闭包就是在一个外函数中定义了一个内函数,内函数里引用了外函数的临时变量,并且外函数的返回值是内函数的引用。
x = 1 def out_func(): x =2 def in_func(): print('in the in_func', x) return in_func f = out_func() f()
# 通过调用__closure__属性查看闭包所包裹的外部变量
print(f.__closure__, f.__closure__[0].cell_contents)
"""
in the in_func 2
(<cell at 0x00A27810: int object at 0x1D9DE320>,) 2
"""
三、装饰器----闭包的运用
1 . 什么是装饰器?
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
2 . 装饰器的使用
-
装饰器的框架
def ver_fun(func): def wrapper(*args, **kwargs): # 引用外部函数变量func # 需要增添的功能逻辑 func() #这里就是将一开始没有装饰的test运行一次 return wrapper @ver_fun # 实际上就相当于 test = ver_fun(test),此时的test就是return回来的wrapper def test(): print('hello world!')
test() # 实际上就是wrapper()
- 无惨装饰器
mport time def timmer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print('函数运行的时间为%s' % (stop_time - start_time)) return wrapper @timmer def test(): time.sleep(2) print('执行完毕') test() """ 执行完毕 函数运行的时间为2.000253677368164 """
当我们调用test函数时,实际上就是在调用wrapper函数,所以当我们的test函数有参数时,wrapper函数也要带上参数,为了统一,我们一般都会在wrapper函数和func函数上加上*args+**kwargs组合,即上述代码可以修改为
import time def timmer(func): def wrapper(*args, **kwargs): start_time = time.time() func(*args, **kwargs) stop_time = time.time() print('函数运行的时间为%s' % (stop_time - start_time)) return wrapper @timmer def test(name, age): time.sleep(2) print('执行完毕') test("jiang", 8)
- 带参数的装饰器
是为装饰器提供多样功能选择的实现提供的,实现原理是三层闭包。如当我们需要不同分认证方式时,可以使用有参装饰器
import os def file_handle(backend_data, res=None, type='fetch'): ''' 这是一个文件处理的函数,对查找和修改功能中的文件处理部分进行的程序解耦 :return: ''' if type == 'fetch': with open('haproxy.conf', 'r') as read_f: tag = False ret = [] for each_line in read_f: if each_line.strip() == backend_data: tag = True continue if tag and each_line.startswith('backend'): break if tag: print(each_line, end='') ret.append(each_line) return ret elif type == 'change': with open('haproxy.conf', 'r') as read_f, \ open('haproxy.conf_new', 'w') as write_f: tag = False write_tag = True for each_line in read_f: if each_line == backend_data: tag = True continue if tag and each_line.startswith('backend'): tag = False if not tag: write_f.write(each_line) elif write_tag: for record in res: write_f.write(record) write_tag = False os.rename('haproxy.conf', 'haproxy.conf.bak') os.rename('haproxy.conf_new', 'haproxy.conf') os.remove('haproxy.conf.bak') def fetch(data): backend_data = 'backend %s' % data return file_handle(backend_data) def add(): pass def change(data): ''' 这是修改功能,用户传入一个列表,列表第一个元素为原修改数据,第二个元素为修改后的数据 :param data: 用户输入的列表 :return: ''' # 提取出用户输入数据中的地址 backend = data[0]['backend'] backend_data = 'backend %s\n' % backend # 将用户中的server拼接起来 old_server_record = '%sserver %s %s weight %s maxconn %s\n' % (' ' * 8, data[0]['record']['server'], data[0]['record']['server'], data[0]['record']['weight'], data[0]['record']['maxconn']) new_server_record = '%sserver %s %s weight %s maxconn %s\n' % (' ' * 8, data[1]['record']['server'], data[1]['record']['server'], data[1]['record']['weight'], data[1]['record']['maxconn']) print('用户想要修改的记录是', old_server_record) print('用户想要修改后的记录是', new_server_record) res = fetch(backend) print(res) if not res or old_server_record not in res: return '用户要修改的数据不存在' else: index = res.index(old_server_record) res[index] = new_server_record res.insert(0, '%s\n' % backend_data) file_handle(backend_data, res=res, type='change') def delete(): pass if __name__ == '__main__': msg = ''' weibo:查询 2:添加 3:修改 4:删除 5:退出 ''' menu_dic = {'weibo': fetch, '2': add, '3': change, '4': delete} while True: print(msg) user_choice = input("请输入选项:").strip() if not user_choice: continue if user_choice == '5': break user_data = input("请输入你想要的数据:").strip() if user_choice != 'weibo': user_data = eval(user_data) res = menu_dic[user_choice](user_data) print(res)
- wraps 的使用
我们这里调用help方法查看一下test函数
print(help(test)) """ Help on function wrapper in module __main__: wrapper(*args, **kwargs) None """
可以看到test函数的函数名为wrapper,证明装饰过后的函数函数名和函数的文档搜发生了变化,如果想保留原有函数的属性,除了我们自己手动在装饰器中修改wrapper的__name__和__doc__属性外,我们还可以调用functools模块提供的装饰器来实现,具体如下:
import time from functools import wraps def timmer(func): @wraps(func) def wrapper(*args, **kwargs): start_time = time.time() func(*args, **kwargs) stop_time = time.time() print('函数运行的时间为%s' % (stop_time - start_time)) return wrapper @timmer def test(name, age): time.sleep(2) print('执行完毕') # test("jiang", 8) print(help(test)) """ Help on function test in module __main__: test(name, age) None """