目录

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()

python flask 运维平台 python 运维开发_迭代器

6.额外知识点:异常处理中try,except用法。

每当在运行时检测到程序错误时,python就会引发异常。对待异常有两种方法:一是可以在程序中捕捉和响应错误;或者忽略已发生的异常。

例如:生成器如果只有3个数据,通过next方法取多余的数据,例第4个数据,就会抛出一个异常 StopIteration。

python flask 运维平台 python 运维开发_生成器_02

 例:通过异常处理,使程序运行不报错。

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

python flask 运维平台 python 运维开发_python_03

二、迭代器(可以被next()函数调用并不断返回下一个值的对象称为迭代器,生成器也是一个迭代器)

1.定义

我们已经知道,可以直接作用于for循环的数据类型有以下几种:一类是集合数据类型,如listtupledictsetstr等;一类是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迭代器类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstrIterable可迭代对象不是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:返回当前作用域内的局部变量和其值组成的字典,或者返回对象的属性列表

python flask 运维平台 python 运维开发_迭代器_04

内置参数详解 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 + 购物商城程序

  1. 额度 15000或自定义
  2. 实现购物商城,买东西加入 购物车,调用信用卡接口结账
  3. 可以提现,手续费5%
  4. 每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息
  5. 支持多账户登录
  6. 支持账户间转账
  7. 记录每月日常消费流水
  8. 提供还款接口
  9. ATM记录操作日志 
  10. 提供管理接口,包括添加账户、用户额度,冻结账户等。。。
  11. 用户认证用装饰器