zoukankan      html  css  js  c++  java
  • 协程函数、面向过程编程、模块与包

    一、协程函数

    1 协程函数的定义

    协程函数就是使用了yield表达式形成的生成器

    实例:

    def eater(name):
        print('%s eat food' %name)
        while True:
            food = yield
        print('done')
    
    g = eater('Yim')
    print(g)
    
    #执行结果:
    <generator object eater at 0x000000000227FEB8>                  #证明g现在是一个生成器函数

    2 协程函数的赋值

    第一阶段:next()初始化函数,让函数暂停在yield位置

    第二阶段:send()给yield传值

    next()和send()都是让函数在上次暂停的位置继续执行。next是让函数初始化,send会给yield赋值并触发下一次代码执行

    实例:

    def eater(name):
        print('%s start to eat food' %name)
        while True:
            food = yield
            print('%s eat %s' %(name,food))
    
    e = eater('Yim')
    
    next(e)                             #初始化,等同于e.send(None)
    e.send('米饭')                      #给yield传值
    e.send('大饼')
    
    #执行结果
    Yim start to eat food
    Yim eat 米饭
    Yim eat 大饼
    def eater(name):
        print('%s start to eat food' %name)
        food_list = []
        while True:
            food = yield food_list
            food_list.append(food)
            print('%s eat %s' %(name,food))
    
    e = eater('Yim')
    
    next(e)                  #初始化
    print(e.send('米饭'))          #1、给yield传值   2、继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当做本次调用的返回值
    print(e.send('大饼'))
    
    #执行结果
    Yim start to eat food
    Yim eat 米饭
    ['米饭']
    Yim eat 大饼
    ['米饭', '大饼']

    3 用装饰器初始化协程函数

    def init(func):
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            next(res)
            return res
        return wrapper
    
    @init
    def eater(name):
        print('%s start to eat food' %name)
        food_list = []
        while True:
            food = yield food_list
            food_list.append(food)
            print('%s eat %s' %(name,food))
    
    e = eater('Yim')
    print(e.send('米饭'))
    print(e.send('大饼'))

    二、 面向过程编程

    面向过程的编程思想:流水线式的编程思想,在设计程序时,需要把整个流程设计出来

    优点:

      体系结构更清晰

      简化程序的复杂度

    缺点:

      可扩展性差

    应用场景:

      Linux内核、git、httpd、shell脚本

    实例:

    # grep -rl 'error' /dir
    
    import os
    
    #初始化装饰器
    def init(func):
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)
            next(g)
            return g
        return wrapper
    
    #1、找到所有文件的绝对路径 --os.walk
    @init
    def search(target):
        while True:
            filepath=yield
            g=os.walk(filepath)
            for pardir,_,files in g:
                for file in files:                      #对最后一个元素进行遍历,这些都是文件
                    abspath=r'%s\%s' %(pardir,file)
                    target.send(abspath)
    
    #2、打开文件 --open
    @init
    def opener(target):
        while True:
            abspath=yield                           #  接收search传递的路径
            with open(abspath,'rb') as f:
                target.send((abspath,f))            # send多个用元组的方式,为了把文件的路径传递下去
    
    #3、循环读取每一行内容 --for line in f
    @init
    def cat(target):
        while True:
            abspath,f=yield #(abspath,f)
            for line in f:
                res=target.send((abspath,line))     # 同时传递文件路径和每一行的内容
                if res:break
    
    #4、过滤关键字 --if 'error' in line
    @init
    def grep(pattern,target):           # # patter是过滤的参数
        tag=False                       #为了去掉重复的文件路径,因为一个文件中存在多个error关键字
        while True:
            abspath,line=yield tag
            tag=False
            if pattern in line:
                target.send(abspath)    # 传递有相应内容的文件路径
                tag=True
    
    #5、打印该行属于的文件名
    @init
    def printer():
        while True:
            abspath=yield
            print(abspath)
    
    g = search(opener(cat(grep('os'.encode('utf-8'), printer()))))
    g.send(r'F:PythonCode')

    三、函数的递归调用

    在调用一个函数的过程中,直接或间接的调用了函数本身,就叫递归调用

    执行有两个阶段:递推和回溯

    递归的效率低,需要在进入下一次递归时保留当前的状态,解决方法是尾递归,即在函数的最后一步(而非最后一行)调用自己,但是python又没有尾递归,且对递归层级做了限制

    1. 必须有一个明确的结束条件

    2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

    3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

    #直接
    def func():
        print('from func')
        func()
    
    func()
    
     
    #间接
    def foo():
        print('from foo')
        bar()
    
    def bar():
        print('from bar')
        foo()
    
    foo()
    # salary(5)=salary(4)+300
    # salary(4)=salary(3)+300
    # salary(3)=salary(2)+300
    # salary(2)=salary(1)+300
    # salary(1)=100
    #
    # salary(n)=salary(n-1)+300     n>1
    # salary(1) =100                       n=1
    
    def salary(n):
        if n == 1:
            return 100
        return salary(n-1)+300
    
    print(salary(5))    

    四、模块

    如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。

    模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是Python标准库的方法

    1 import语句

    import加载的模块分为四个通用类别:

    1. 使用Python编写的代码(.py文件)
    2. 已被编译为共享库或DLL的C或C++扩展
    3. 包好一组模块的包
    4. 使用C编写并链接到Python解释器的内置模块

    import语法:

           import module1[, module2[,... moduleN]

    当解释器遇到import语句,如果模块在当前的搜索路径就会被导入,搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support,需要把命令放在脚本的顶端:

    support.py 文件代码为:

    def print_func( par ):
        print ("Hello : ", par)
      return

    test.py引入support模块:

    import support              #导入模块
    
    support.print_func('Yim')   #现在可以调用模块里包含的函数了
    
    #执行结果
    Hello :  Yim

    还可以给模块起一个别名:

    import support as s1
    
    s1.print_func('Yim')

    一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行(可以使用sys.modules查看验证):

    import support
    import support
    import support
    
    support.print_func('Yim')
    
    #执行结果:
    Hello :  Yim

    2 from…import*语句

    优点:使用源文件内的名字时无需加前缀,使用方便

    缺点:容易与当前文件的名称空间内的名字混淆

    把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

    from modname import *               #不推荐使用

    下面只导入support模块里面的print_func函数:

    from support import print_func      #可以用逗号分隔,写上多个函数名
    
    print_func('Yim')                   #直接执行print_func函数

    也可以起别名:

    from support import print_func as p1
    
    p1('Yim')

    可以使用__all__来控制*,修改support.py文件:

    __all__ = [print_func]                  #可以在列表内添加多个函数名
    
    def print_func( par ):
        print ("Hello : ", par)
        return

    test.py文件如下:

    from support import *
    
    print_func('Yim')                       #执行结果报错,NameError: name 'print_func' is not defined

    3 模块的搜索路径

    搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块

    搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块中的path变量

    模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块

    需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名

    import sys
    
    print(sys.path)
    
    #执行结果:
    ['F:\Python\Code\模块', 'F:\Python', 'C:\Python36\python36.zip', 'C:\Python36\DLLs', 'C:\Python36\lib', 'C:\Python36', 'C:\Python36\lib\site-packages']

    在脚本中修改sys.path,引入一些不在搜索路径中的模块

    import sys
    
    print(sys.path)
    
    sys.path.append('F:\Python\Code\不常用模块')
    # sys.path.insert(0,'F:\Python\Code\不常用模块')        #也可以使用insert,排在前的目录会优先被搜索
    
    print(sys.path)
    
    #执行结果
    ['F:\Python\Code\模块', 'F:\Python\2017-18s', 'C:\Python36\python36.zip', 'C:\Python36\DLLs', 'C:\Python36\lib', 'C:\Python36', 'C:\Python36\lib\site-packages']
    ['F:\Python\Code\模块', 'F:\Python\2017-18s', 'C:\Python36\python36.zip', 'C:\Python36\DLLs', 'C:\Python36\lib', 'C:\Python36', 'C:\Python36\lib\site-packages', 'F:\Python\Code\不常用模块']

    以下是官方文档:

    #官网链接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
    搜索路径:
    当一个命名为spam的模块被导入时
        解释器首先会从内建模块中寻找该名字
        找不到,则去sys.path中找该名字
    
    sys.path从以下位置初始化
        1 执行文件所在的当前目录
        2 PYTHONPATH(包含一系列目录名,与shell变量PATH语法一样)
        3 依赖安装时默认指定的
    
    注意:在支持软连接的文件系统中,执行脚本所在的目录是在软连接之后被计算的,换句话说,包含软连接的目录不会被添加到模块的搜索路径中
    在初始化后,我们也可以在python程序中修改sys.path,执行文件所在的路径默认是sys.path的第一个目录,在所有标准库路径的前面。这意味着,当前目录是优先于标准库目录的,需要强调的是:我们自定义的模块名不要跟python标准库的模块名重复,除非你是故意的

    4 __name__属性

    一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。

    if __name__ == '__main__':                      #文件当做脚本运行时,__name__ 等于'__main__'
       print('程序自身在运行')
    else:
       print('我来自另一模块')
    
    #执行结果
    程序自身在运行 
    #交互式,导入模块
    >>> import support
    我来自另一模块

    说明: 每个模块都有一个__name__属性,当其值是'__main__'时,表明该模块自身在运行,否则是被引入。

    常用实例:

    def func1():
        print('from func1')
    
    def func2():
        print('from func2')
    
    if __name__ == '__main__':
       func1()
       func2()

    五、包

    包是一种管理 Python 模块命名空间的形式,采用"点模块名称",比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B

    无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,这都是关于包才有的导入语法

    包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)

    强调:

    1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
    2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块

    有一个包结构如下:

    glance/                     #顶层包
    ├── __init__.py        #初始化 glance 包
    ├── api
    │   ├── __init__.py
    │   ├── policy.py

    导入包里的子模块:

    import glance.api.policy

    必须使用全名去访问:

    glance.api.policy.func()

    还有一种导入模块的方法是:

    from glance.api import policy               #从模块中导入一个指定的部分到当前命名空间中

    它不需要那些冗长的前缀,所以可以这样使用:

    policy.func()

    还有一种就是直接导入一个函数或者变量:

    from glance.api.policy import func

    可以直接使用func()函数:

    func()

    1 导入包实例

    包结构如下:

    glance/                     #顶层包
    run.py                      # 执行文件
    ├── __init__.py        #初始化 glance 包
    ├── api
    │   ├── __init__.py
    │   ├── policy.py     

    导入包:

    #1、run.py用全名访问func(),内容如下:
    import glance
    glance.api.policy.func()
    # glance/__init__.py如下:
    import glance.api                           #也可以用from glance import api
    # glance/api/__init__.py如下:
    import glance.api.policy                    #也可以用from glance.api import policy
    
    #2、run.py直接访问func(),内容如下:
    import glance
    glance.func()
    # glance/__init__.py如下:
    from glance.api.policy import func          #不能用import glance.api.policy.func
    # glance/api/__init__.py可以不用写

    当使用from package import item这种形式的时候,对应的item既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。

    import语法会首先把item当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,恭喜,一个:exc:ImportError 异常被抛出了。

    反之,如果使用形如import item.subitem.subsubitem这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。

    注意事项:

    1. 关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则
    2. 对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)
    3. 对比import item 和from item import name的应用场景:如果我们想直接使用name那必须使用后者。

    2 绝对导入和相对导入

    以上面的例子是绝对导入的方式

    绝对导入:以glance作为起始

    相对导入:用.或者..的方式作为起始(只能在一个包中使用,不能用于不同目录内)

    实例:

    run.py直接访问func(),内容如下:
    import glance
    glance.func()
    # glance/__init__.py如下:
    from .api.policy import func        #1、这样的好处是:就算包名glance被更改也不会有影响,只需改run.py内的import …2、..表示上级目录,可以导入上级目录中的其他模块

    特别需要注意的是:

    • 可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入
    • 包的相对导入只能用from的形式

    六、软件开发规范

     

    bin:存放执行脚本
    conf:存放配置文件
    core:存放核心逻辑
    db:存放数据库文件
    lib:存放自定义的模块与包
    log:存放日志

    start.py内容:

    import os
    import sys
    
    # print(os.path.abspath(__file__))                       #获取当前文件的绝对路径,F:softin	est.py
    # print(os.path.dirname(os.path.abspath(__file__)))       #获取当前文件所在的目录,F:softin
    # print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))      #获取当前文件所在的目录的上级目录,F:soft
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)                                   #将soft这个包的路径加到环境变量
    
    from core import core
        ……                      #导入其他模块
    
    if if __name__ == '__main__':
        ……                      #执行
  • 相关阅读:
    Threejs学习 一
    Mapbox的表达式
    mapbox 不加载地图
    SQL Server将查询出数据进行列转行操作
    SQL Server 常用近百条SQL语句(收藏版)
    SQL Server DATEDIFF() 函数用法
    SQL Server 数据库开启日志CDC记录,导致SQL Server 数据库日志异常增大
    查询SQL Server数据库使用的版本号信息
    windows 无法启动 SQL Server (MSSQLSERVER) 服务(位于本地计算机上)。错误 1069由于登入失败而无法启动 。
    SQL Server 不同数据间建立链接服务器进行连接查询
  • 原文地址:https://www.cnblogs.com/yanmj/p/7275650.html
Copyright © 2011-2022 走看看