生成器 迭代器,装饰器 ,软件开发规范

一、生成器

     1、列表生成式

          有列表data=[1,2,3,4],要求将列表data里面的数据均加1:

     除了循环,可以用列表生成式一行写出替代多行的循环

1 data=[1,2,3,4]
2 data=[i+1 for i in data]
3 print(data)

          生成式中也可以写三元运算

#需求:列表中小于3的都*2,大于等于3的+1
data=[i*2 if i<3 else i+1 for i in data]

    列表生成式,是python内置的一种直接生成list的表达式,可以直接生成一个列表,但是受内存限制,容量有限,如果生成的数据量很大,但是使用到的寥寥无几,那么其他数据占用的控件就会浪费,那么有没有节省控件的方法呢?。。。。。。。生成器 generator   

  2.生成器generator

        生成器生成的方式有两种,一种是将列表生成式的‘[]’改为‘()’,一种是听过在函数中加入yield关键字

        生成器和列表生成式的区别:列表生成式在生成的时候就已经计算出了参数的一个列表,而生成器被调用的时候,才会去生成参数

       (1)通过()生成生成器

1 data=[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=[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={
2     'name':'宋晓楠',
3     'age':22
4 }
5 f=open('file','w',encoding='utf-8')
6 data=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': 'u5b8bu6653u6960', 'age': 22}"
f.write(data_json) 

          ②json.dump(a,f) 序列化文件之后,直接将序列化的内容写入到文件f中

data={
    '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=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:UserssongxiaonanPycharmProjectsday05_learn
#C:UserssongxiaonanPycharmProjects
#E:pythonpython35.zip
#E:pythonDLLs
#E:pythonlib
#E:python
#E:pythonlibsite-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、常见问题说明








原文地址:https://www.cnblogs.com/songxiaonan/p/6040851.html