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


    一、生成器

         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、常见问题说明








  • 相关阅读:
    React Virtual Dom 与 Diff
    打造前端CI/CD工作流
    webpack-chain明细
    React项目中实现多语言支持
    【WPF】大量Canvas转换为本地图片遇到的问题
    【C#】【分享】 XX分钟学会C#
    【WPF】一些拖拽实现方法的总结(Window,UserControl)
    【WPF】 InkCanvas 书写毛笔效果
    js中this指向问题
    js原型浅谈理解
  • 原文地址:https://www.cnblogs.com/songxiaonan/p/6040851.html
Copyright © 2020-2023  润新知