首页 > 代码库 > 生成器 迭代器,装饰器 ,软件开发规范
生成器 迭代器,装饰器 ,软件开发规范
一、生成器
1、列表生成式
有列表data=http://www.mamicode.com/[1,2,3,4],要求将列表data里面的数据均加1:
除了循环,可以用列表生成式一行写出替代多行的循环
1 data=http://www.mamicode.com/[1,2,3,4] 2 data=http://www.mamicode.com/[i+1 for i in data] 3 print(data)
生成式中也可以写三元运算
#需求:列表中小于3的都*2,大于等于3的+1 data=http://www.mamicode.com/[i*2 if i<3 else i+1 for i in data]
列表生成式,是python内置的一种直接生成list的表达式,可以直接生成一个列表,但是受内存限制,容量有限,如果生成的数据量很大,但是使用到的寥寥无几,那么其他数据占用的控件就会浪费,那么有没有节省控件的方法呢?。。。。。。。生成器 generator
2.生成器generator
生成器生成的方式有两种,一种是将列表生成式的‘[]’改为‘()’,一种是听过在函数中加入yield关键字
生成器和列表生成式的区别:列表生成式在生成的时候就已经计算出了参数的一个列表,而生成器被调用的时候,才会去生成参数
(1)通过()生成生成器
1 data=http://www.mamicode.com/[1,2,3] 2 gen1=[i+1 for i in data]# gen1为一个列表生成式 3 gen2=(i+1 for i in data)#gen2为一个生成器 4 print(gen1)# 打印列表生成式 会输出一个列表:[2, 3, 4] 5 print(gen2)# 直接打印生成器 可以看到这时候只是生成了一个对象 ,还未进行运算:<generator object <genexpr> at 0x0000000001157048>
既然不能直接打印生成器,那么如何取得生成器生成的参数呢?
两种方法:1、gen._next_()/next(gen) 2、for 循环
1 print(gen2.__next__())#输出2 2 print(next(gen2))#输出3 3 4 for i in gen2: 5 print(i)#输出 4,如果没有上面两次调用,则for循环调用生成器得的结果就是:2,3,4
(2)函数中加入yield关键字
例子:斐波那契数列 除了第一个数和第二个数外,其他数都是前两个数的和 1,1,2,3,5,8,13,21,34..........,要求打印该数列的前8个数
1 def fbq(max): 2 n,a,b=0,0,1 3 while n<max: 4 print(b) 5 a,b=b,a+b 6 n=n+1 7 fbq(8)
现在将上面的函数变为一个生成器:
def fbq(max): n,a,b=0,0,1 while n<max: yield b a,b=b,a+b n=n+1 f=fbq(8) print(f) print(f.__next__()) for i in f: print(i)
程序执行到yiled的时候,就会暂停,再次调用生成器函数的时候便会在上次暂停的位置,继续往下执行。即yield保存了程序的中间状态
在单线程下,生成器还可以模拟并行运算:
def conumer(name): print(‘%s准备吃包子‘%name) while True: baozi=yield #注意:此处yield无参数 print(‘包子%s做好了,%s开始吃包子‘%(baozi,name)) def producer(name): c=conumer(‘A‘) c2=conumer(‘B‘) c.__next__() c2.__next__() print(‘_____________________‘) for i in range(3): c.send(i) #给yield传参数 c2.send(i) producer(‘songxiaonna‘)
A准备吃包子 B准备吃包子 _____________________ 包子0做好了,A开始吃包子 包子0做好了,B开始吃包子 包子1做好了,A开始吃包子 包子1做好了,B开始吃包子 包子2做好了,A开始吃包子 包子2做好了,B开始吃包子
二、迭代器
可直接进行for循环的数据类型: 列表 元组 集合 字符串,还有生成器,统称为可迭代对象。
判断对象时候可以迭代:isinstance
from collections import Iterable #导入模块 print(isinstance([],Iterable)) # 返回true
生成器不仅可以通过for循环,还可以用next调用,可以被next函数调用,并不断返回下一个值的对象,统称为迭代器 iteratior
定义一个迭代器:iter
data=http://www.mamicode.com/[1,2,3] data=iter(data) print(data) # data:<list_iterator object at 0x0000000000565CC0> print(next(data))# 1
迭代器的对象,表示的是一个数据流,我们不知道数据流的长度,迭代器的计算是惰性运算,按需运算,但是列表等可迭代对象是长度,提前就已经知道列表中的数据,所以列表、 元组、 集合、 字符串不是迭代器
三、装饰器
装饰器的本事就是函数,其功能是为其他函数添加附加功能,实现之前函数没有的功能
假如,当前有10个函数,需要再这10个函数每个函数增加一个记录日志的功能,可以另外定义一个函数logger(),然后每个函数去调用这个函数.......
以上的方法可以解决问题,但是如果是线上程序,那么修改程序源代码会带来很大的风险。所以为程序的新添加一个功能,需要遵循两个原则:
1、不修改被装饰函数的源代码
2、不修改被装饰函数的调用方式
装饰器对于被装饰函数是完全透明的
高阶函数+嵌套函数=>装饰器
(1)函数即‘变量’
def foo(): print(‘in the foo ‘) bar() foo() def bar(): print(‘in the bar‘) #输出会报错:应为执行foo()的时候,bar函数还没有定义,正确的代用可以更改为: def foo(): print(‘in the foo ‘) bar() def bar(): print(‘in the bar‘) foo() 或者将bar函数写在 foo前面
定义bar函数就相当于将bar函数体复制子给bar变量名。 只要在调用之前,函数已经存在,就可已被调用
(2)高阶函数
满足下面两个条件之一就是高阶函数
1、把一个函数名当做实参传给另外一个函数
def bar(): print(‘in the bar‘) def test1(func): func() test1(bar)
2、返回值中包含函数名
def test2(func): return func() test2(bar)
(3)嵌套函数
在一个函数的函数体内,用def再声明一个函数
def foo(): def bar(): #该函数只在foo函数中生效 局部变量 print(‘in the bar‘) bar() foo()
1 x=0 2 def foo(): 3 x=1 4 def fo(): 5 x=2 6 def f(): 7 x=3 8 print(x) 9 f() 10 fo() 11 foo() 12 #输出结果为3 作用于只能从里往外找
1、不带参数的装饰器:
原有test1和test2两个函数,现在需要需要新增一个计算每个函数运行时间的功能(已经写好新增功能的函数为timmer)
1 import time 2 def timmer(func):#高阶函数 3 start_time=time.time() 4 func() 5 end_time=time.time() 6 print(‘函数%s运行的时间为:%s‘%(func,(end_time-start_time))) 7 8 def test1(): 9 time.sleep(1) 10 print(‘in the test1‘) 11 def test2(): 12 time.sleep(2) 13 print(‘in the test2‘) 14 15 timmer(test2)#计算test2函数的运行时间,将函数test2当做实参传入到timmer中
新增了timmer功能,但是改变了之前函数test2的调用方法......
不想改变函数的调用方式,可以将test2函数替换,test2=timmer(test2),前提是timmer函数中要返回test2的内存对象,要将func()改为 return func,如下:
import time def timmer(func): start_time=time.time() return func #之前这块的代码: func () end_time=time.time() print(‘函数%s运行的时间为:%s‘%(func,(end_time-start_time))) def test1(): time.sleep(1) print(‘in the test1‘) def test2(): time.sleep(2) print(‘in the test2‘) test2 = timmer(test2)#注意:将timmer(test2)赋值给test2的时候就已经在调用timmer函数了 test2()
还是不可行,因为调用到return()的时候,函数就不会往下执行了,那么有没有完美的解决方案呢?.....................嵌套函数,即在新功能函数外面再套一层函数
1 import time 2 #在之前新加功能函数的外面套一层函数 3 def timmer2(func): 4 def timmer(): 5 start_time=time.time() 6 func() #不需要再用return 7 end_time=time.time() 8 print(‘函数%s运行的时间为:%s‘%(func,(end_time-start_time))) 9 return timmer #返回timmer函数的内存地址 10 11 def test1(): 12 time.sleep(1) 13 print(‘in the test1‘) 14 def test2(): 15 time.sleep(2) 16 print(‘in the test2‘) 17 test2 =timmer2(test2) 18 test2()
有一种更为直观的书写方法,将 test2 =timmer2(test2) 用@+函数名的方式替换掉,如下: 在需要加新功能的功能函数前面加上 @+新功能函数名
import time def timmer2(func): def timmer(): start_time=time.time() func() end_time=time.time() print(‘函数%s运行的时间为:%s‘%(func,(end_time-start_time))) return timmer
@timmer2 # 相当于test2 =timmer2(test2) def test1(): time.sleep(1) print(‘in the test1‘) def test2(): time.sleep(2) print(‘in the test2‘) test1()
2、带参数的装饰器:
只需要再timmer函数加上*args和**kwarg即可
import time def timmer2(func): def timmer(*args,**kwargs): #给timmer加上非固定参数 start_time=time.time() func(*args,**kwargs) #。。。加上非固定参数 end_time=time.time() print(‘函数%s运行的时间为:%s‘%(func,(end_time-start_time))) return timmer @timmer2 def test1(): time.sleep(1) print(‘in the test1‘)
@timmer2
def test2(name,age): #test为带参函数
print(name,age) test2(‘宋晓楠‘,18)
3、小练习:
需求:现在有一个网站,一个页面为一个函数
def index():
print(‘___首页____‘)
def home():
print(‘____用户中心____‘)
def bbs()
print(‘____发帖页面____‘)
现在需要在 用户中心页面和发帖页面增加登录功能
1 def auth(func): 2 def login(): 3 name=‘songxiaonan‘ 4 pwd=‘123‘ 5 Name=input(‘请输入用户名:‘) 6 Pwd=input(‘密码:‘) 7 if Name==name and Pwd==pwd: 8 func() 9 else: 10 print(‘用户名或密码错误............‘) 11 return login 12 13 14 def index(): 15 print(‘___首页____‘) 16 @auth 17 def home(): 18 print(‘____用户中心____‘) 19 def bbs(): 20 print(‘____发帖页面____‘) 21 22 home()
四、json&pickle的序列化
如果有一个字典dict,需要将这个字典写入一个新文件,则必须将字典转为字符串才可以写入
序列化:将一个内存对象转为字符串,即序列化
反序列化:将字符串转回为对应的内存对象,及反序列化
1 data=http://www.mamicode.com/{ 2 ‘name‘:‘宋晓楠‘, 3 ‘age‘:22 4 } 5 f=open(‘file‘,‘w‘,encoding=‘utf-8‘) 6 data=http://www.mamicode.com/str(data) #写入一个新文件的内容必须为字符串或者二进制,所以将一个内存对象写入硬盘,必须先转为字符串或者二进制 7 f.write(data)
(1)json 模块
1、序列化 ①json.dumps(data)
#json序列化 json.dumps(data) import json data={ ‘name‘:‘宋晓楠‘, ‘age‘:22 } f=open(‘file‘,‘wb‘,encoding=‘utf-8‘) data=str(data) data_json=json.dumps(data) print(data_json) #输出:"{‘name‘: ‘\u5b8b\u6653\u6960‘, ‘age‘: 22}" f.write(data_json)
②json.dump(a,f) 序列化文件之后,直接将序列化的内容写入到文件f中
data=http://www.mamicode.com/{ ‘name‘:‘宋晓楠‘, ‘age‘:22 } f=open(‘file‘,‘wb‘,encoding=‘utf-8‘) json.dump(data,f) # ==f.wirte(json.dumps(data))
2、反序列化 ①json.loads(data_json)
data_loads=json.loads(data_json) #输出结果 将已经被转为字符串的字典 又转为字典
②json.load(f)
f=open(‘file‘,‘rb‘,encoding=‘utf-8‘) data=json.load(f) #=data=http://www.mamicode.com/json.loads(f.read()) print(data)
注意:json支持所有语言,但是json只支持简单的数据类型的序列化,不支持复杂的数据类型,比如:函数
(2)pickle
pickle只支持python语言,但是可以支持复数据类型之间的转换
pickle的用法和json的用法完全一样
如何dumps或者load是多次? no!写程序时dumps一次 loads一次,要么就dump多个文件就ok了
五、模块
(1)导入自己写的模块
不在同一个脚本中,想共享同一个代码,可以将功能代码转为模块
①import auth 注意:不需要加.py
②调用auth里面的login方法: auth.login
③导入auth模块中的login,hehe方法: from auth import login,hehe
④导入自己写的模块的时候,如果模块和当前文件不在同一个目录下,则:from 模块所在目录 import login
(2)模块路径
#查看python搜索模块的路径集 sys.path import sys print(sys.path)#输出一个列表 #输出: for i in sys.path: print(i) #输出: #C:/Users/songxiaonan/PycharmProjects/day05_learn #C:\Users\songxiaonan\PycharmProjects\day05_learn #C:\Users\songxiaonan\PycharmProjects #E:\python\python35.zip #E:\python\DLLs #E:\python\lib #E:\python #E:\python\lib\site-packages
1 import sys,os 2 print(__file__)#答应当前文件的相对路径 3 print(os.path.dirname(__file__))#去掉路径中的文件名 4 print(os.path.dirname(os.path.dirname(__file__)))#再去掉一层 5 print(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))#相对路径变为绝对路径
进度条:
import sys,time for i in range(10): sys.stdout.write(‘*‘) sys.stdout.flush() time.sleep(0.3)
六、软件开发目录结构规范
Foo/
|-- bin/
| |-- foo #启动脚本
|
|-- foo/ 软件代码目录
| |-- tests/ 测试脚本目录
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py 程序主入口
|
|-- docs/ 文档
| |-- conf.py
| |-- abc.rst
|
|-- setup.py 安装脚本
|-- requirements.txt 程序依赖的第三方包
|-- README
readme:
1、软件基本功能
2、运行代码的方法:安装环境,启动命令等
3、使用说明
4、代码目录结构说明,也可以说明软件的基本原理
5、常见问题说明
生成器 迭代器,装饰器 ,软件开发规范