目录
Day 4 本节内容:
一、生成器(只有被调用时才会生成对应数据;将函数创建为生成器可以随时中断函数,去同时做一些其他的功能,然后再进入函数继续执行。)
1.列表生成式
2.生成器定义
3.生成器创建方法1:
4.生成器创建方法2:
5.生成器用例:通过yield实现在单线程的情况下实现并发运算的效果,异步IO
6.额外知识点:异常处理中try,except用法。
二、迭代器(可以被next()函数调用并不断返回下一个值的对象称为迭代器,生成器也是一个迭代器)
1.定义
2.小结
三、装饰器(不修改被装饰函数的源代码和调用方式,实现附加功能的添加)
1.定义:
2.原则:函数感知不到装饰器的存在
3.实现装饰器知识点:1.函数即变量 2.高阶函数 3.嵌套函数
四、内置函数
五、json & pickle数据序列化
1.序列化与反序列化定义,及json和pickle的使用
2.json与pickle的区别(属性用法一致)
六、软件目录结构规范
七、作业:
Day 4 本节内容:
1.迭代器 & 生成器
2.装饰器
3.内置函数
4.json & pickle数据序列化
5.软件目录结构规范
6.作业:ATM项目开发
一、生成器(只有被调用时才会生成对应数据;将函数创建为生成器可以随时中断函数,去同时做一些其他的功能,然后再进入函数继续执行。)
1.列表生成式
#使用列表生成式创建列表 [2,4,6,8,10]
# 方式1.
list1 = [a * 2 for a in range(1,6)]
print(list1)
# 方式2.
def functest(num):
b = 2
return(num * b)
list2 = [functest(a) for a in range(1,6)]
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,直接创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
2.生成器定义
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
列表表达式:直接把列表所有元素全部生成;
生成器:1.只有在调用时才会生成对应数据
2.只记录当前位置,不可取前面过去的数据
3.只有一个 __next__() 方法,用来获取下一个数据;python2中为 next()
3.生成器创建方法1:
只要把一个列表生成式的[]
改成()
,就创建了一个generator:
# 创建生成器
generator = (a * 2 for a in range(1,6))
print(generator) # <generator object <genexpr> at 0x00000190A44D94A0>
# 调用生成器
generator.__next__() # 第一次调用,值为2,但是不会输出
print(generator.__next__()) # 第二次调用,输出4
print('hello') # hello
for i in generator:
print(i) # 输出 6 8 10,因为生成器只记录当前位置,不会重新调用
4.生成器创建方法2:
如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator。将函数变为生成器的好处,可以随时中断函数(去同时做一些其他的功能)然后再进入函数继续执行。
def func():
for n in range(1,4):
print('hello')
yield
print(f'你好这是生成器{n}')
gener = func()
gener.__next__()
gener.__next__()
gener.__next__()
# 执行逻辑
1. def func() 定义函数
2. gener = func() 创建生成器gener
3. gener.__next__() # n=1;---输出“hello”;---遇到yield中断退出
4. gener.__next__() # 返回继续执行生成器;---输出“你好这是生成器1”;---n=2;---输出“hello”;---遇到yield中断退出
5. gener.__next__() # 返回继续执行生成器;---输出“你好这是生成器2”;---n=3;---输出“hello”;---遇到yield中断退出
yield:保存当前生成器函数状态并可以返回值(yield 10,类似return 10),中断函数退出。
__next__() :回来继续执行生成器函数
send():可以传递值给yield,然后回来继续执行生成器函数
5.生成器用例:通过yield实现在单线程的情况下实现并发运算的效果,异步IO
#通过生成器实现协程并行运算,吃包子和做包子两个配合同步进行
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True: # 确保生成器可以一直被调用执行下去
baozi = yield
print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
def make():
c1 = consumer('A')
c2 = consumer('B')
c1.__next__()
c2.__next__()
print("老子开始准备做包子啦!")
for i in range(1,4):
time.sleep(1)
print("做了2个包子!")
c1.send(i)
c2.send(i)
make()
6.额外知识点:异常处理中try,except用法。
每当在运行时检测到程序错误时,python就会引发异常。对待异常有两种方法:一是可以在程序中捕捉和响应错误;或者忽略已发生的异常。
例如:生成器如果只有3个数据,通过next方法取多余的数据,例第4个数据,就会抛出一个异常 StopIteration。
例:通过异常处理,使程序运行不报错。
def func():
for n in range(1,4):
print('hello')
yield 10
print(f'你好这是生成器{n}')
return '这里遇到一个生成器调用错误信息!!!'
gener = func()
while True:
try:
gener.__next__()
gener.__next__()
gener.__next__()
gener.__next__()
except StopIteration as errorinfo:
print('程序运行返回StopIteration错误:',errorinfo.value)
break
二、迭代器(可以被next()
函数调用并不断返回下一个值的对象称为迭代器,生成器也是一个迭代器)
1.定义
我们已经知道,可以直接作用于for
循环的数据类型有以下几种:一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;一类是generator
,包括生成器和带yield
的generator function。
1.1这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
# 可以使用isinstance()
判断一个对象是否是Iterable可迭代
对象:
from collections.abc import Iterable
isinstance([1,2,3],Iterable)
# True
isinstance(100,Iterable)
# False
而生成器不但可以作用于for
循环,还可以被next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了。
1.2可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
# 可以使用isinstance()
判断一个对象是否是Iterator
对象:
from collections.abc import Iterable,Iterator
isinstance([1,2,3],Iterator)
# False
isinstance((x for x in range(10)),Iterator)
# True
2.小结
凡是可作用于for
循环的对象都是Iterable可迭代对象
类型;
凡是可作用于next()
函数的对象都是Iterator迭代器
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable可迭代对象
但不是Iterator迭代器
,不过可以通过iter()
函数获得一个Iterator
对象。
from collections.abc import Iterable,Iterator
isinstance([1,2,3],Iterator)
# False
num = iter([1,2,3]) # 通过iter()函数,将可迭代对象列表变为迭代器
isinstance(num,Iterator)
# True
print(num.__next__()) # 1
print(num.__next__()) # 2
三、装饰器(不修改被装饰函数的源代码和调用方式,实现附加功能的添加)
1.定义:
本质是函数(装饰其它函数),就是为其他函数添加附加功能。
高阶函数 + 嵌套函数 =》装饰器
2.原则:函数感知不到装饰器的存在
*1.不能修改被装饰的函数的源代码
*2.不能修改被装饰的函数的调用方式
3.实现装饰器知识点:1.函数即变量 2.高阶函数 3.嵌套函数
*1.函数即变量:函数调用之前都有定义就可以执行成功(定义函数相当于定义一个变量,将函数体在内存中指向一个地址;)
定义、调用变量
x = 1
y = 2
print(y,x)
定义、调用函数
def x():
print(1)
y()
def y():
print(2)
x()
# 1
# 2
*2.高阶函数:a.把一个函数名当做实参传给另外一个函数(在不修改被装饰函数的源代码情况下为其添加功能);b.返回值中包含函数名(不修改被装饰函数的调用方式)
a.在不修改被装饰函数的源代码情况下为其添加功能
import time
def func1():
time.sleep(3)
print('in the func1')
def func2(func_name):
start_time = time.time()
func_name()
stop_time = time.time()
print(f'the func1 run time is {stop_time-start_time} !')
func2(func1)
# 输出
in the func1
the func1 run time is 3.0013177394866943 !
# 实现一个查看程序运行时间的附加功能
############################################################
b.不修改被装饰函数的调用方式
import time
def func1():
print('in the func1')
def func2(func_name):
print('hello')
return func_name
1.
func2(func1) #相当于要实现的附加功能
#输出
hello
2.
test1 = func2(func1)
print(test1) #实现附加功能,同时返回要被装饰函数func1的内存地址 用于调用函数
#输出
hello
<function func1 at 0x000002AD8F68E160>
test1() #调用test1,同时实现附加功能和被修饰函数的功能
#输出
hello
in the func1
***3.覆盖掉要被装饰的函数,实现不改变其调用方式
func1 = func2(func1)
func1()
#输出
hello
in the func1
*3.嵌套函数:在一个函数体内创建另外一个函数(创建新函数,并非调用),这种函数就叫嵌套函数。
高阶函数 + 嵌套函数 =》装饰器
例:装饰器timer,为test1和test2函数增加显示运行时间的功能
import time
# 嵌套函数 + 高阶函数 = 组成装饰器 timer
def timer1(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print('the function run time is %s'% (stop_time-start_time))
return deco
@timer1 # 调用timer装饰器装饰test1函数;相当于执行 test1=timer(test1)
def test1():
time.sleep(3)
print('It is test1 func')
test1()
# 执行步骤:
# 1.定义函数timer --》 2.执行@timer,相当于执行test1 = timer(test1) --》
# 3.test1 = timer(test1) = deco;返回deco函数的内存地址 --》4.执行test1() = 执行deco()
_____________________timer1_____________________
注意:
当test1函数无参数,test2函数需要参数时,用装饰器同时装饰这两个函数;给deco函数添加'*args,**kwargs'参数
def timer2(func):
def deco(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
stop_time = time.time()
print('the function run time is %s'% (stop_time-start_time))
return deco
@timer2
def test2(name,age):
time.sleep(3)
print('It is test2 func')
print(name,age)
test2('XiaoZz',18)
@timer2
def test3():
time.sleep(3)
print('It is test3 func')
test3()
_____________________timer2_____________________
装饰器例子:
# 给三个页面添加登录验证功能,并且使用不同的登录方式
# 1.定义三种不同登录方式的账号密码
local_name,local_password = 'local','password'
ldap_name,ldap_password = 'ldap','password'
other_name,other_password = 'other','password'
# 2.定义装饰器login函数,添加参数login_type判断不同的登录方式
def login(login_type):
# 一级嵌套函数,func参数传递'被装饰的函数'
def out_deco(func):
# 二级嵌套函数,实现新增的功能与'被装饰的函数'功能
def deco(*args,**kwargs):
user_username = input('请输入你的用户名称:').strip()
user_password = input('请输入你的用户密码:').strip()
# 判断不同的登录方式,实现不同的效果
if login_type == 'local':
default_username = local_name
default_password = local_password
if user_username == default_username and user_password == default_password:
print('欢迎你,登陆成功!')
func(*args,**kwargs) # 执行被装饰参数
else:
print('账号或密码错误,退出!')
return
# 判断不同的登录方式,实现不同的效果
if login_type == 'ldap':
default_username = ldap_name
default_password = ldap_password
if user_username == default_username and user_password == default_password:
print('欢迎你,登陆成功!')
func(*args,**kwargs) # 执行被装饰参数
else:
print('账号或密码错误,退出!')
return
# 判断不同的登录方式,实现不同的效果
if login_type == 'other':
default_username = other_name
default_password = other_password
if user_username == default_username and user_password == default_password:
print('欢迎你,登陆成功!')
func(*args,**kwargs) # 执行被装饰参数
else:
print('账号或密码错误,退出!')
return
return deco # 返回deco函数内存地址
return out_deco # 返回out_deco函数内存地址
@login(login_type='local') # 添加参数login_type选择不同的对应登录方式
def page1():
print('hello,there is page 1 !')
print('---------- page1 function local ----------')
page1()
@login(login_type='ldap') # page2 = login()
def page2(name):
print('hello %s,there is page 2 !!' % name)
print('---------- page2 function ldap ----------')
page2('XiLong')
@login(login_type='other')
def page3(name1,name2):
print('hello %s and %s,there is page 3 !!!' %(name1,name2))
print('---------- page3 function other ----------')
page3('XiaoLong','XiaoJin')
四、内置函数
1.数学运算
abs:求数值的绝对值
>>> abs(-2)
2
max/min:返回可迭代对象中的元素中的最大值/ 最小值 或者所有参数的最大值/ 最小值
>>> max(1,2,3) # 传入3个参数 取3个中较大者
3
>>> max('1234') # 传入1个可迭代对象,取其最大元素值
'4'
>>> max(-1,0) # 数值默认去数值较大者
0
>>> max(-1,0,key = abs) # 传入了求绝对值函数,则参数都会进行求绝对值后再取较大者
-1
pow:返回两个数值的幂运算值或其与指定整数的模值
>>> pow(2,3)
>>> 2**3
8
>>> pow(2,3,5)
>>> pow(2,3)%5
>>> 2**3%5
3
round:对浮点数进行四舍五入求值
>>> round(1.1314926,1)
1.1
>>> round(1.1314926,5)
1.13149
sum:对元素类型是数值的可迭代对象中的每个元素求和
# 传入可迭代对象
>>> sum((1,2,3,4))
10
# 元素类型必须是数值型
>>> sum((1.5,2.5,3.5,4.5))
12.0
>>> sum((1,2,3,4),-9)
1
2.类型转换
bool:根据传入的参数的逻辑值创建一个新的布尔值
>>> bool() #未传入参数
False
>>> bool(0) #数值0、空序列等值为False
False
>>> bool(1)
True
int:根据传入的参数创建一个新的整数
>>> int() #不传入参数时,得到结果0。
0
>>> int(3)
3
>>> int(3.6)
3
float:根据传入的参数创建一个新的浮点数
>>> float() #不提供参数的时候,返回0.0
0.0
>>> float(3)
3.0
>>> float('3')
3.0
str:返回一个对象的字符串表现形式(给用户)
>>> str()
''
>>> str(None)
'None'
>>> str('abc')
'abc'
>>> str(123)
'123'
bytearray:根据传入的参数创建一个新的字节数组
>>> bytearray('中文','utf-8')
bytearray(b'\xe4\xb8\xad\xe6\x96\x87')
bytes:根据传入的参数创建一个新的不可变字节数组
>>> bytes('中文','utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
ord:返回Unicode字符对应的整数
>>> ord('a')
97
chr:返回整数所对应的Unicode字符
>>> chr(97) #参数类型为整数
'a'
bin:将整数转换成2进制字符串
>>> bin(3)
'0b11'
oct:将整数转化成8进制数字符串
>>> oct(10)
'0o12'
hex:将整数转换成16进制字符串
>>> hex(15)
'0xf'
iter:根据传入的参数创建一个新的可迭代对象
tuple:根据传入的参数创建一个新的元组
list:根据传入的参数创建一个新的列表
set:根据传入的参数创建一个新的集合
frozenset:根据传入的参数创建一个新的不可变集合
enumerate:根据可迭代对象创建枚举对象
>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1)) #指定起始值
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
3.序列操作
all:判断可迭代对象的每个元素是否都为True值
any:判断可迭代对象的元素是否有为True值的元素
sorted:对可迭代对象进行排序,返回一个新的列表
>>> a = ['a','b','d','c','B','A']
>>> a
['a', 'b', 'd', 'c', 'B', 'A']
>>> sorted(a) # 默认按字符ascii码排序
['A', 'B', 'a', 'b', 'c', 'd']
>>> sorted(a,key = str.lower) # 转换成小写后再排序,'a'和'A'值一样,'b'和'B'值一样
['a', 'A', 'b', 'B', 'c', 'd']
4.对象操作
help:返回对象的帮助信息
dir:返回对象或者当前作用域内的属性列表
id:返回对象的唯一标识符
hash:获取对象的哈希值
type:返回对象的类型,或者根据传入的参数创建一个新的类型
len:返回对象的长度
ascii:返回对象的可打印表字符串表现方式
vars:返回当前作用域内的局部变量和其值组成的字典,或者返回对象的属性列表
内置参数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii
五、json & pickle数据序列化
1.序列化与反序列化定义,及json和pickle的使用
序列化:将对象转换成易传输或易保存的数据的过程称为序列化;即把Python对象转换成JSON字符串。例如将内存上的对象变成字符串可以存到硬盘上。
json.dumps()
json.dump() 将Python对象转换成json字符串并存储到文件中
反序列化:将一定的数据格式转成可用的对象称为反序列化;即把JSON字符串转换成python对象。例如将硬盘上的数据对象变成原来格式,可以再恢复到内存中。
json.loads()
json.load() 读取指定文件中的json字符串并转换成Python对象
# 如果需要序列化的数据中存在中文,就需要将dumps方法的 ensure_ascii 参数设置为False,否则显示的中文会乱码。
import json
# 序列化:
name = ['校长','老师']
test1 = json.dumps(name,ensure_ascii=False)
print(test1) #["校长", "老师"]
print(type(test1)) # <type 'str'>
file = open('abc.txt','w')
json.dump(name,file)
# 反序列化
test2 = json.loads(test1)
print(test2[0]) # 校长
print(type(test2)) #<type 'list'>
json.load(file)
2.json与pickle的区别(属性用法一致)
json序列化之后得到的是字符串,仅支持字典和列表对象,各种编程语言几乎都能支持 json。
pickle序列化之后得到的是字节,支持Python中大部分对象,仅被Python支持。
pickle序列化不会改变字典键的数据类型;json序列化,如果键是数字会转为字符串。
六、软件目录结构规范
1.例如开发一个 App 项目,目录结构应该如下:
App/
|-- bin/ # 存放项目的一些可执行文件
| |-- app.py # 项目启动脚本,调用程序入口 main.py
|
|-- app/ # 存放项目的所有源代码
| |-- tests/ # 存放单元测试代码
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py # 程序的入口
|
|-- conf/ # 存放一些配置文档
| |-- app_conf.py # 配置文件
| |-- abc.rst
|
|-- log/ # 存放日志文件
| |-- app.log # 运行日志
| |-- error.log # 错误日志
|
|-- setup.py # 安装、部署、打包的脚本
|-- requirements.txt # 存放软件依赖的外部Python包列表
|-- README # 项目说明文件
2.如何通过 bin/app.py 启动脚本去直接调用 main.py 呢?
# 在 bin/app.py 中
import os
import sys
print(__file__) # 打印相对路径
print(os.path.abspath(__file__)) # 打印绝对路径
print(os.path.dirname(os.path.abspath(__file__))) # 获取路径名,去掉文件名
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 再使用一次path.dirname方法获取上一级路径
sys.path.append(BASE_DIR) # 添加环境变量,这时就可以直接调用 main.py 和 app_conf.py 中的函数了
from conf import app_conf
from app import main
3.注意:不应当在代码中直接 import
来使用"app_conf.py"或"main.py"。应该可以通过给 main.py
启动参数 -c 指定配置路径的方式来让程序读取配置内容。
七、作业:
作业需求:模拟实现一个ATM + 购物商城程序
- 额度 15000或自定义
- 实现购物商城,买东西加入 购物车,调用信用卡接口结账
- 可以提现,手续费5%
- 每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息
- 支持多账户登录
- 支持账户间转账
- 记录每月日常消费流水
- 提供还款接口
- ATM记录操作日志
- 提供管理接口,包括添加账户、用户额度,冻结账户等。。。
- 用户认证用装饰器