python学习之路-day4-装饰器&json&pickle
本节内容
- 迭代器&生成器
- 装饰器
- Json & pickle 数据序列化
一、生成器
1、列表生成式
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
2、生成器
只有在调用时才会生成相应的数据,只有一个方法:__next__()方法。next(),也就是又next方法的
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
1 >>> L = [x * x for x in range(10)] 2 3 >>> L 4 5 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 6 7 >>> g = (x * x for x in range(10)) 8 9 >>> g 10 11 <generator object <genexpr> at 0x1022ef630>
创建L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值:
1 >>> next(g) 2 3 >>> next(g) 4 5 >>> next(g) 6 7 >>> next(g) 8 9 >>> next(g) 10 11 >>> next(g) 12 13 >>> next(g) 14 15 >>> next(g) 16 17 >>> next(g) 18 19 >>> next(g) 20 21 >>> next(g) 22 23 Traceback (most recent call last): 24 25 File "<stdin>", line 1, in <module> 26 27 StopIteration
如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator:
普通函数:
1 1 def fib(max): 2 2 n,a,b = 0,0,1 3 3 while n<max: 4 4 print(b) 5 5 #yield b 6 6 a,b=b,a+b 7 7 n = n+1 8 8 return 'done' 9 9 t = fib(11) 10 10 print(t) 11 11 输出结果: 12 12 1 13 13 1 14 14 2 15 15 3 16 16 5 17 17 8 18 18 13 19 19 21 20 20 34 21 21 55 22 22 89 23 23 done 24 25 上面函数变成生成器,只需要把print(b)改为yield b就可以了: 26 27 28 def fib(max): 29 30 n,a,b = 0,0,1 31 32 while n<max: 33 34 #print(b) 35 36 yield b 37 38 a,b=b,a+b 39 40 n = n+1 41 42 return 'done' 43 44 t = fib(11) 45 46 print(t) 47 48 49 50 输出结果: 51 52 <generator object fib at 0x00000000010F59E8>
注:赋值语句:a, b
=
b, a
+
b
相当于:
1 t = (b, a + b) # t是一个tuple 2 a = t[0] 3 b = t[1]
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
变成生成器之后并没有运行,而是在要输出的时候才运行:(每次next,输出一次)
def fib(max): n,a,b = 0,0,1 while n<max: #print(b) yield b #碰到yield,程序中断, 出到函数外面在遇到next的时候在回来接着往下走 a,b=b,a+b n = n+1 return 'done' t = fib(11) print(t) print(t.__next__()) print(t.__next__()) print(t.__next__()) print("---------") print(t.__next__()) 输出结果: 1 1 2 --------- 3
再来一例:
1 def consumer(name): 2 print("%s 准备吃包子啦!" %name) 3 while True: 4 baozi = yield #碰到yield退出程序,遇到c.__next__()时在进来继续往下走 5 print("包子[%s]来了,被【%s】吃了!" %(baozi,name)) 6 c = consumer("lsp") 7 c.__next__() 8 c.__next__() 9 c.__next__() 10 c.__next__() 11 12 输出结果: 13 lsp 准备吃包子啦! 14 包子[None]来了,被【lsp】吃了! 15 #yield是none值,可以通过send给yield传值,见下例 16 包子[None]来了,被【lsp】吃了! 17 包子[None]来了,被【lsp】吃了!
**最后一例**
import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield #碰到yield退出程序,遇到c.__next__()或者(send)时在进来继续往下走(只要有yield就是生成器) print("包子[%s]来了,被【%s】吃了!" %(baozi,name)) def producer(name): c=consumer('lw') #因为是生成器,所以不会输出任何东西 c2 = consumer('lsp') c.__next__() #输出lw准备吃包子了 c2.__next__() #输出lsp准备吃包子了 print("我开始准备做包子了!") #输出我开始做包子了 for i in range(3): #循环 time.sleep(1) #sleep 1秒 print("做了1个包子") #做了一个包子 c.send(i) #将i的值传给yield c2.send(i) #将i的值传给yield producer("alex") #调用函数 输出结果: lw 准备吃包子啦! lsp 准备吃包子啦! 我开始准备做包子了! 做了1个包子 包子[0]来了,被【lw】吃了! 包子[0]来了,被【lsp】吃了! 做了1个包子 包子[1]来了,被【lw】吃了! 包子[1]来了,被【lsp】吃了! 做了1个包子 包子[2]来了,被【lw】吃了! 包子[2]来了,被【lsp】吃了!
二、迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
只要有next的方法,就一定是一个迭代器
for line in f: #就是一个迭代器
接上面生成器的例子:
1 def fib(max): 2 n,a,b = 0,0,1 3 while n<max: 4 #print(b) 5 yield b 6 a,b=b,a+b 7 n = n+1 8 return 'done' 9 t = fib(11) 10 for i in t: 11 print(i) 12 输出结果: 13 1 14 1 15 2 16 3 17 5 18 8 19 13 20 21 21 34 22 55 23 89
三、装饰器
装饰器:本质是函数(装饰其他函数),就是为其他函数添加附加功能
原则:
- 不能修改被修饰的函数的源代码
- 不能修改被装饰的函数的调用方式
原函数代码和调用方式 不变,不知道有装饰器在装饰他(一句话,源函数代码和调用方式一点都不变,实现改变功能的函数,即是装饰器)
例一:
import time def bar(): #原函数 time.sleep(3) print('in the bar') def test1(func): start_time=time.time() func() #调用函数bar,因为func= bar,所以这里func()== bar(),即调用上面的函数 stop_time=time.time() print("the func time is %s" %(stop_time-start_time)) #bar() #原函数调用方式 test1(bar) #调用函数test1,func = bar ,这样虽然实现了修改原函数功能,但是调用方式改变了
例二:
1 原函数: 2 import time 3 def test1(): # 原函数1 4 print("this is test1") 5 def test2(): #原函数2 6 print("this is test2") 7 test1() #原函数调用方式 8 test2() #原函数调用方式 9 ----------------------------- 10 加装饰器(加功能:程序运行时间) 11 12 import time 13 def timer(func): 14 def deco(): 15 start_time = time.time() 16 func() #test1 = func()--->test1() //#test2 = func()--->test2() 17 stop_time = time.time() 18 print("the func run time is %s" %(stop_time-start_time)) 19 return deco #将deco的函数的内存地址返回,就是test1 == deco函数的内存地址! 20 @timer #test1=timer(test1) test1 == deco函数的内存地址! 21 def test1(): #test1 == deco函数的内存地址!test1()即执行deco函数,真正的test1()到deco函数里面遇到func()才会执行 22 time.sleep(1) 23 print("this is test1") 24 @timer #test2=timer(test1) 25 def test2(): 26 time.sleep(1) 27 print("this is test2") 28 test1() 29 test2()
终极装饰器:
1 # __author__ = 'lw' 2 import time 3 user,passwd = 'lw','lw123' 4 def auth(auth_type): 5 print('auth_func:',auth_type) 6 def outer_wrapper(func): 7 def wrapper(*args,**kwargs): 8 print("wrapper func args:",*args,**kwargs) 9 if auth_type == "local": 10 username = input("Username:").strip() 11 password = input("password:").strip() 12 if username == user and passwd == password: 13 print("\033[32;1mUser has passwd authentication\033[0m") 14 res = func(*args,**kwargs) #from home 15 print("---after authentication") 16 return res 17 else: 18 exit("\033[31;1mInvalid username or password\033[0m") 19 elif auth_type == "ldap": 20 print("welcome to ldap") 21 return wrapper 22 return outer_wrapper 23 def index(): 24 print("welcome to index page") 25 @auth(auth_type="local") #home = wrapper() 26 def home(): 27 print("welcome to home page") 28 return "from home" 29 def bbs(): 30 print("welcome to bbs page") 31 index() 32 print(home()) #wrapper() 33 bbs()
四、 json&&pickle
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript(Standard ECMA-262 3rd Edition - December 1999)的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成。
json在所以语言中通用
引用模块
import json
重要函数
- 编码:把一个Python对象编码转换成Json字符串 json.dumps()
- 解码:把Json格式字符串解码转换成Python对象 json.loads()
例子
序列化
将内存中的信息写入到文件
#!/usr/bin/python import json info = { 'name':'alex', 'age':'22' } f = open("test.txt","w") f.write(json.dumps(info)) f.close() [root@test1 python]#
反序列化
将文件中的信息读取出来
[root@test1 python]# cat fanxuliehua.py #!/usr/bin/python import json f = open("test.txt","r") data = json.loads(f.read()) print(data) print(data["name"]) [root@test1 python]# python fanxuliehua.py {u'age': u'22', u'name': u'alex'} alex [root@test1 python]#
json只能序列化简单的数据类型,不能反序列化函数等复杂的数据类型,想要序列化复杂的数据,需要使用pickle,用法和json完全一样,只是序列化的时候变成二进制类型,即在打开文件的时候要用wb和rb
pickle.dump(info,f) ==f.write(pickle.dumps(info))