装饰器
在不改变函数和函数调用方法的前提下,实现扩充函数的功能。其依赖三个基本要素
1. 函数即变量
2. 高阶函数
3. 嵌套函数
函数即变量
函数与变量的定义基本原理都是将内容存放到内存,将变量名和函数名作为指针指向内容的内存空间地址,唯一的区别就是变量在内空间存放的是变量值,而函数在内存空间存放的是函数体,其本质上也就是一块字符串
高阶函数
高阶函数包含两大特征
以一个函数名作为高阶函数的参数
1 2 3 4 5 6 7 8 9 |
def deco(func): print("Starting function") func() def test(): print("Testing") # 使用函数test作为函数deco的参数,则函数deco是一个高阶函数 deco(test) |
高阶函数返回值可以是一个函数名(即函数所在的内存空间地址)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def deco(func): print("Starting function") return func def test(): print("Testing") # 运行deco(test)将打印一行信息后返回test函数的内存空间地址,deco是一个高阶函数 myfunc = deco(test) # 运行myfunc()实际上是运行deco(test)返回的内存地址中的函数体,即test函数的内容 myfunc() test() # 执行test函数 |
如果将上面例子中的myfunc = deco(test)修改为 test = deco(test), 则相当于替换了函数test的内存地址,同时deco函数体内继续用test()的方式调用函数而不影响原有功能,这就是装饰器的雏形
嵌套函数
在函数体中再定义一个函数,这个函数的作用域仅在当前函数中
1 2 3 4 5 6 7 8 9 10 |
def deco(): # 定义内部函数test, 此函数仅能在deco内部访问 def test(): print("Testing") # 返回这个内部函数的内存地址 return test deco() # 直接访问test函数将报错 # test() |
上面例子中调用deco()将返回内部函数test所在的内存空间地址,通过print(deco())可以看到返回类似以下的结果
1 |
<function deco.<locals>.test at 0x00000130675D29D8> |
将这个返回值赋予一个变量,再执行这个变量,即可执行这个内部函数的函数体
1 2 3 4 5 6 7 8 |
def deco(): # 定义内部函数test, 此函数仅能在deco内部访问 def test(): print("Testing") return test func = deco() # deco()返回test函数的内存地址,赋值给变量func,此时func也就成了一个函数(函数即变量) func() # 调用函数 |
装饰器基础版
结合上面三个部分的内容,高阶函数的两种特征和嵌套函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 定义高阶函数deco,接收一个函数func作为参数(即函数内存地址) def deco(func): # 定义内部函数wrapper,打印一条信息后,调用从父函数继承来的参数func并执行 def wrapper(): print("From warpper") func() # 调用作为参数传入的函数(即下面原本的origin函数) # 返回wrapper函数的内存地址 return wrapper def origin(): print("From origin") # 先将originh函数的内存地址作为参数传递给deco函数,然后origin函数的内存地址被替换成deco函数返回的wrapper函数的内存地址 origin = deco(origin) #执行函数origin,实际上是执行了函数wrapper的函数体 origin() |
以上例子已经完全实现了装饰器的基本要求,即:
1. 不改变函数本身
2. 不改变函数的调用方式
3. 实现对原函数的功能扩展
同时对于origin = deco(origin)这种表达式,python提供了专门的装饰器专用语法,在定义函数的上面加上
1 2 3 |
@deco # 加载装饰器 def origin(): print("From origin") |
装饰器进阶版
基本版的装饰器实现了简单的装饰功能,但如果原函数是带参数的,还需要稍作修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 定义高阶函数deco,接收一个函数func作为参数(即函数内存地址) def deco(func): def wrapper(*args, **kwargs): # 接收原函数内存地址被替换后传递过来的参数 print("From warpper") func(*args, **kwargs) # 参数重新传递给原函数以保证功能的一致性 # 返回wrapper函数的内存地址 return wrapper @deco def origin(name): print("%s From origin" % name) # 调用origin函数实际上等同于调用了wrapper函数,参数name同样也传递给了函数wrapper origin("Bob") |
装饰器终极版
多样化的装饰器,装饰不同函数时赋予不同的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
def out_deco(func_type): # 定义高阶函数deco,接收一个函数func作为参数(即函数内存地址) def deco(func): def wrapper(*args, **kwargs): # 接收原函数内存地址被替换后传递过来的参数 if func_type == "N": print("Mode N") if func_type == "A": print("Mode A") func(*args, **kwargs) # 参数重新传递给原函数以保证功能的一致性 # 返回wrapper函数的内存地址 return wrapper return deco ''' out_deco(N)函数返回值为deco,@out_deco(N)等同于@deco,即还是以deco作为真正的装饰器(origin1 = deco(origin1)),只是是参数func_type会向下继承; deco(origin1)执行后返回wrapper,即origin1 = wrapper,那么origin1("参数") = wrapper("参数"),实现最终的装饰; 由于参数_type的继承,可以根据最初指定装饰器时的参数执行不同动作 ''' @out_deco("N") def origin1(name): print("Welcome %s" % name) @out_deco("A") def origin2(age): print("Age is %s" % age) # 调用origin1函数实际上等同于调用了wrapper函数,参数name同样也传递给了函数wrapper origin1("Bob") origin2(18) |
生成器generator
列表生成式
1 2 |
x = [ i*2 for i in range(10)] # i*2可以是其他更复杂的表达式,如将将i传递给函数func(i) print(x) |
生成器
1 2 3 4 5 6 7 8 9 10 11 12 13 |
y = (i*2 for i in range(10)) print(type(y)) # 生成器每次循环只生成当前需要读取的值放入内存 for i in y: print(i) # 使用__next__方法访问生成器的下一个值 z = (i*2 for i in range(5)) print("Var z".center(20, "=")) print(z.__next__()) print(z.__next__()) print(next(z)) print(next(z)) |
函数式生成器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 斐波那契数列函数 def fib(maxnum): ''' 表达式a, b = b, a + b实际上相当于 t = (b, a + b) a = t[0] b = t[1] ''' n, a, b = 0, 0, 1 while n < maxnum: print(b) a, b = b, a + b n = n + 1 return 'Done' print("Fuction fib".center(20, "=")) fib(10) |
使用yield关键字将函数变成一个生成器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
print("Generator fib".center(20, "=")) def fib(maxnum): n, a, b = 0, 0, 1 while n < maxnum: # yield 将函数变成了一个generator yield b a, b = b, a + b n = n + 1 # 返回值用于生成器的StopIteration异常时返回值 return 'Done' f = fib(10) print(f) print(f.__next__()) print(f.__next__()) print(f.__next__()) print(f.__next__()) for i in f: print(i) |
yield进阶示例
一个协程式的消费者-生存者雏形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import time # 定义消费者生成器 def consumer(name): print("【%s】准备吃包子啦" % name) while True: baozi = yield print("包子【%s】号上桌了,被【%s】吃了一口" % (baozi, name)) def producer(): # 生成两个消费者并初始化 c1 = consumer("张三") c2 = consumer("李四") c1.__next__() c2.__next__() # 循环制作10个包子 for i in range(1,10): print("包子【%s】号制作完成" % i) time.sleep(1) # send方法将包子传递给yield作为值 c1.send(i) c2.send(i) producer() |
迭代器
可迭代对象
列表,字典,元组,字符串都是可迭代对象,数字不是
1 2 3 4 5 6 7 |
from collections import Iterable print(isinstance([], Iterable)) # True print(isinstance({}, Iterable)) # True print(isinstance(1234, Iterable)) # False print(isinstance("abcd", Iterable)) # True print(isinstance((x for x in range(10)), Iterable)) # True |
迭代器
可以通过next方法获取下一个对象
1 2 3 4 5 6 7 8 9 10 |
from collections import Iterator print(isinstance([1, 2, 3, 4], Iterator)) # False print(isinstance((x for x in range(10)), Iterator)) # True # iter函数将一个可迭代对象转换成一个迭代器 it = iter([1, 2, 3, 4]) print(isinstance(it, Iterator)) # True print(next(it)) print(next(it)) print(it.__next__()) |
序列化
json序列化
json序列号只能处理简单的字符类型的对象,如字符串、列表、字典等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import json dic1 = { "广州": { "花都": "001", "越秀": "002", "海珠": "003", "黄埔": "004" }, "深圳": { "宝安": "011", "龙华": "012", "福田": "013" } } # json序列化 with open("json.dump", "w") as fw: print(json.dumps(dic1)) fw.write(json.dumps(dic1)) # 等同于json.dump(dic1, fw) # json反序列化 with open("json.dump", "r") as fr: data = json.loads(fr.read()) # 等同于json.load(dic1, fr) print(data) |
pickle序列化
pickle序列化与json序列化用法基本一直,但pickle序列化支持几乎所有Python数据类型(不支持生成器),pickle序列化后的对象为二进制对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import pickle dic1 = { "广州": { "花都": "001", "越秀": "002", "海珠": "003", "黄埔": "004" }, "深圳": { "宝安": "011", "龙华": "012", "福田": "013" }, "iter1": iter([1, 2, 3, 4]) # 迭代器对象 } # pickle序列化 with open("pickle.dump", "wb") as fw: print(pickle.dumps(dic1)) fw.write(pickle.dumps(dic1)) # 等同于pickle.dump(dic1, fw) # pickle反序列化 with open("pickle.dump", "rb") as fr: data = pickle.loads(fr.read()) # 等同于pickle.load(dic1, fr) print(data) # 还原迭代器对象并调用其__next__方法 print(data["iter1"].__next__()) print(data["iter1"].__next__()) |
跨目录调用
1 2 3 4 5 6 7 8 9 10 11 12 |
# 导入必要模块 import os import sys # 获取程序根目录 root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 加入环境变量 sys.path.append(root_path) # 导入其他目录的模块 from conf import config from main import main |
原文链接:Python 从入门到放弃 - Lesson 4 迭代器、装饰器、序列化,转载请注明来源!