zoukankan      html  css  js  c++  java
  • 模块的概念、使用,搜索路径与优先级,软件开发的目录规范

    一、模块的概念

    1、什么是模块

     ……模块就是一系列功能的集合体

     模块大致分为四种类别:

        1、一个py文件就是一个模块,文件名叫test.py,模块名叫test

        2、一个包含有__init__.py文件的文件夹称之为包,包也是模块

        3、使用C编写并链接到python解释器的内置模块

        4、已被编译为共享库或DLL的C或C++扩展

      模块的三种来源:

        1、自带的模块

        2、第三方模块:pip3 install requests

        3、自定义的模块

    2、为何要用模块

    1)、(自带的模块,第三方模块)拿来主义,提升开发效率

    2)、自定义模块 ---> 是为了解决代码冗余的问题

    3、如何用模块

    模块都是被导入使用的。以spam.py为例来介绍模块的使用:文件名为spam.py,模块名为spam

    #spam.py
    print('from the spam.py')
    
    money=1000
    
    def read1():
        print('spam模块:',money)
    
    def read2():
        print('spam模块')
        read1()
    
    def change():
        global money
        money=0
    

    二、模块的使用

    import语句:

    要想在另外一个py文件中引用foo.py中的功能,需要使用import foo,首次导入模块会做三件事:

    1、执行源文件代码

    2、产生一个新的名称空间用于存放源文件执行过程中产生的名字

    3、在当前执行文件所在的名称空间中得到一个名字foo,该名字指向新创建的模块名称空间,若要引用模块名称空间中的名字,需要加上该前缀,如下

    加上spam.作为前缀就相当于指名道姓地说明要引用spam名称空间中的名字,所以肯定不会与当前执行文件所在名称空间中的名字相冲突,并且若当前执行文件的名称空间中存在money,执行spam.read1()或spam.change()操作的都是源文件中的全局变量money

    '''
    首次导入模块发生的事情
    1、运行spam.py,创建一个模块的名称空间,将spam.py运行过程中产生的名字都丢到模块的名称空间中
    2、在当前名称空间中得到一个名字,该名字是指向模块的名称空间
    '''
    import spam  # 导入模块spam
    import spam
    import spam
    # print(spam)  # <module 'spam' from 'E:\PYTHON\pycharm project\project\day14\模块的使用1\spam.py'>
    # ps:后续的导入直接引用首次的导入的成功,不会重复执行spam.py,不会重复创建名称空间
    print(spam.money)  # 引用模块spam中变量money的值
    print(spam.read1)  # <function read1 at 0x00000267C1C2A0D0>
    print(spam.read2)  # <function read2 at 0x00000267C1C2A310>
    print(spam.change)  # <function change at 0x00000267C1C2A1F0>
    
    spam.read1()  # 调用模块spam的read1()函数
    spam.read2()
    # >>>:
    # spam模块
    # spam模块: 1000
    
    money = 2000
    spam.change()
    print(money)  # 2000
    print(spam.money)  # 0
    

    导入规范:

    通常情况下所有的导入语句都应该写在文件的开头,然后分为三部分:

    第一部分:先导入自带的模块

    第二部分:导入第三方

    第三部分:导入自定义的

    import的其他用法(as):

    import os,sys,re  # 在一行导入,用逗号分隔开不同的模块
    import spam as sm  # 在当前位置为导入的模块起一个别名
    

    from-import语句:

    from...import...与import语句基本一致

    唯一不同的是:使用import spam导入模块后,引用模块中的名字都需要加上spam.作为前缀。

    而使用from sapm import money,read1,read2,change则可以在当前执行文件中直接引用模块spam中的名字

    from spam import money
    print(money)   # 1000  直接使用模块spam中的money
    '''
    无需加前缀的好处是使得我们的代码更加简洁,
    坏处则是容易与当前名称空间中的名字冲突,
    如果当前名称空间存在相同的名字,
    则后定义的名字会覆盖之前定义的名字。
    ''' 
    money = 20    # 将来自于spam模块的money覆盖了
    print(money)  # 20  # 引用的是当前作用域的money
    

    from-import的其他用法:

    # 起别名
    from spam import money,read1,read2,change
    from spam import money as m,read1 as r1,read2 as r2,change as cg
    print(m)
    print(r1)
    print(r2)
    
    # from... import *
    from spam import *  # 把spam中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字
    print(money)
    print(read1)
    print(read2)
    print(change)
    
    模块的编写者可以在自己的文件中定义__all__变量用来控制*代表的意思
    #spam.py
    print('from the spam.py')
    __all__=['money','read1']  # 该列表中所有的元素必须是字符串类型,每个元素对应spam.py中的一个名字
    money=1000
    
    def read1():
        print('spam模块:',money)
    
    def read2():
        print('spam模块')
        read1()
    
    def change():
        global money
        money=0
    

    循环导入问题

    循环导入问题指的是在一个模块加载/导入的过程中导入另外一个模块,而在另外一个模块中又返回来导入第一个模块中的名字,由于第一个模块尚未加载完毕,所以引用失败、抛出异常,究其根源就是在python中,同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会去重复执行内部代码

    解决方案:

    # 方案一:导入语句放到最后,保证在导入时,所有名字都已经加载过
    # 文件:m1.py
    print('正在导入m1')
    
    x='m1'
    
    from m2 import y
    
    # 文件:m2.py
    print('正在导入m2')
    y='m2'
    
    from m1 import x
    
    # 文件:run.py内容如下,执行该文件,可以正常使用
    import m1
    print(m1.x)
    print(m1.y)
    
    # 方案二:导入语句放到函数中,只有在调用函数时才会执行其内部代码
    # 文件:m1.py
    print('正在导入m1')
    
    def f1():
        from m2 import y
        print(x,y)
    
    x = 'm1'
    
    # 文件:m2.py
    print('正在导入m2')
    
    def f2():
        from m1 import x
        print(x,y)
    
    y = 'm2'
    
    # 文件:run.py内容如下,执行该文件,可以正常使用
    import m1
    
    m1.f1()
    

    注意:循环导入问题大多数情况是因为程序设计失误导致,上述解决方案也只是在烂设计之上的无奈之举,在我们的程序中应该尽量避免出现循环/嵌套导入,如果多个模块确实都需要共享某些数据,可以将共享的数据集中存放到某一个地方,然后进行导入

    三、模块的搜索路径与优先级

    模块其实分为四个通用类别:

      1、使用纯python代码编写的py文件

      2、包含一系列模块的包

      3、使用C编写并链接到Python解释器中的内置模块

      4、使用C或C++编译的扩展模块

    模块的搜索路径优先级为:

      1、内存中已经导入好的

      2、内置

      3、sys.path sys.path也被称为模块的搜索路径,它是一个列表类型

    注意:

    列表中的每个元素其实都可以当作一个目录来看:在列表中会发现有.zip或.egg结尾的文件,

    二者是不同形式的压缩文件,事实上Python确实支持从一个压缩文件中导入模块,

    我们也只需要把它们都当成目录去看即可。

    四、软件开发的目录规范

    1、区分py文件的两种用途:

    一个python文件有两种用途:

    1、被当主程序脚本执行
    
    2、被当做模块导入
    

    为了区别同一个文件的不同用途,每个py文件都内置了__name__变量,该变量在py文件被当做脚本执行时赋值为"main",在py文件被当做模块导入时赋值为模块名。

    作为模块spam.py的开发者,可以在文件末尾基于__name__在不同应用场景下值的不同来控制文件执行不同的逻辑

    if __name__ == '__main__':
        # spam.py被当做脚本执行时运行的代码
        pass
    else:
        # spam.py被当做模块时运行的代码
        pass
    

    通常我们会在if的子代码块中编写针对模块功能的测试代码,这样spam.py在被当做脚本运行时,就会执行测试代码,而被当做模块导入时则不用执行测试代码。

    我们在编写py文件时,需要时刻提醒自己,该文件既是给自己用的,也有可能会被其他人使用,因而代码的可读性与易维护性显得十分重要,为此我们在编写一个模块时最好按照统一的规范去编写,如下:

    #!/usr/bin/env python #通常只在类unix环境有效,作用是可以使用脚本名来执行,而无需直接调用解释器。
    
    "The module is used to..." #模块的文档描述
    
    import sys #导入模块
    
    x=1 #定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能
    
    class Foo: #定义类,并写好类的注释
        'Class Foo is used to...'
        pass
    
    def test(): #定义函数,并写好函数的注释
        'Function test is used to…'
        pass
    
    if __name__ == '__main__': #主程序
        test() #在被当做脚本执行时,执行此处的代码
    

    2、软件开发目录规范

    规范化开发:

    一个py文件中:1、文件加载问题
    
           2、代码可读性差,查询麻烦
    
    要将一个py文件分开,合理的分成多个py文件
    

    settings.py:配置文件

    就是放置一些项目中需要的静态参数,比如文件路径,数据库配置,软件的默认设置等
    

    common.py: 公共组件文件

    这里面放置一些我们常用的公共组件函数,并不是我们核心逻辑的函数,而更像是服务于整个程序中的公用的插件,程序中需要即调用。比如我们程序中的装饰器auth,有些函数是需要这个装饰器认证的,但是有一些是不需要装饰器认证的,它既是何处需要何处调用即可,比如还有密码加密功能,序列化功能,日志功能等这些功能都可以放在这里
    

    src.py:主逻辑文件

    这个文件主要存放的就是核心逻辑功能,你看你需要进行选择的这些核心功能函数,都应该放在这个文件
    

    start.py:项目启动文件

    你的项目需要有专门的文件启动,而不是在你的核心逻辑部分启动的,目的就是放在显眼的位置,方便启动,start.py文件中引用sys模块,动态获取目录
    

    log.py : 日志文件

    存储log日志的文件,日志主要是供开发人员使用,比如你的项目中出现一些bug问题,比如开发人员对服务器做的一些操作都会记录到日志中,以便开发者浏览,查询。
    

    类似于register文件:

    这个文件文件名不固定,register只是我们项目中用到的注册表,但是这种文件就是存储数据的文件,类似于文本数据库,那么我们一些项目中的数据有的是从数据库中获取的,有些数据就是这种文本数据库中获取的,总之,你的项目中有时会遇到一些数据存储在文件中,与程序交互的情况,所以我们要单独设置这样的文件
    

    设计一个层次清晰的目录结构,就是为了达到以下两点:

    1、可读性高:不熟悉这个项目的代码的人,一眼就能看懂目录结构,直到程序启动脚本是哪个,测试目录在哪儿,配置我就爱你在哪儿等,从而非常快速的了解这个项目
    2、可维护性高:定义好组织规划后,维护者就能很明确的知道,新增的哪个文件和代码应该放在什么目录之下,这个好处是,随着时间的推移,代码配置的规模增加,项目结构不会混乱,仍然能够组织良好。
    
  • 相关阅读:
    杭电 Problem
    杭电Problem 5053 the sum of cube 【数学公式】
    杭电 Problem 2089 不要62 【打表】
    杭电 Problem 4548 美素数【打表】
    杭电 Problem 2008 分拆素数和 【打表】
    杭电 Problem 1722 Cake 【gcd】
    杭电 Problem 2187 悼念512汶川大地震遇难同胞——老人是真饿了【贪心】
    杭电Problem 1872 稳定排序
    杭电 Problem 1753 大明A+B
    东北林业大 564 汉诺塔
  • 原文地址:https://www.cnblogs.com/caodan01/p/14231477.html
Copyright © 2011-2022 走看看