文章目录

  • 主要内容:
  • Python 模块初识
  • 对比C语言的 include
  • 模块也是对象
  • 模块的搜索路径
  • 理解 "命名空间"
  • import语句
  • import as语句
  • from-import 语句
  • 导入模块意味着 "被执行"
  • 理解 "导入" 和 "加载"
  • 模块的内置变量
  • 包(Package)
  • 主要内容:

    理解模块和包的基本概念
    理解模块的导入过程
    熟悉sys模块和os模块中的一些重要操作


    Python 模块初识

    当代码量比较大的时候, 我们最好把代码拆分成一些有组织的代码片段.
    每个代码片段里面包含一组逻辑上有关联的函数或者类.
    每一个片段里放在一个独立的文件中. 这样的片段就称为模块(module)
    使用 import 可以在一个Python文件中引入其他的模块.

    对比C语言的 include

    回忆我们C语言中, 如果一个文件想使用其他文件的功能, 通过 #include 的方式来引入一个头文件.
    C语言的 #include 本质上是文本替换. 因此可能会导致一系列的问题**(重复包含, 包含顺序相关).**

    所以通常加上:

    #pragma once 	//防止头文件重复包含
    

    模块也是对象

    Python的模块则避免了C语言中的这些问题. import 实际上是创建了一个模块对象. 通过这个对象来访问
    模块中的具体方法.

    import os
    print(type(os))	#<class 'module'>  模块
    print(id(os))	#2191830213400  有id,所以也是一个对象
    

    模块的搜索路径

    我们尝试 import 一个模块名时, Python解释器必须要知道模块文件的路径

    例如, 我们此处有一个add.py, 那么对应的模块名就是add (去掉后缀)


    sys 是python标准库自带的模块,一些和python解释器相关的模块

    Python从sys.path中查找模块.

    我们可以看到, sys.path其实是一个列表. 里面里面的内容是Python的一些默认搜索路径. 如果我们想再加
    入一些新的路径, 直接修改sys.path即可. ->sys.path.append('....')

    import sysq	
    print(sys.path)
    #执行结果:
    ['E:\\Python文件存储', 'E:\\pythonProject1', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages']
    
    import sys
    for line in sys.path:
        print(line)
    #执行结果:
    E:\Python文件存储
    E:\pythonProject1
    C:\Users\Mango\AppData\Local\Programs\Python\Python37\python37.zip
    C:\Users\Mango\AppData\Local\Programs\Python\Python37\DLLs
    C:\Users\Mango\AppData\Local\Programs\Python\Python37\lib
    C:\Users\Mango\AppData\Local\Programs\Python\Python37
    C:\Users\Mango\AppData\Local\Programs\Python\Python37\lib\site-packages
    

    import语句不一定非要写在源代码文件的最开始(推荐写在最上面)

    如果在多个目录中, 都存在相同名字的模块, 这个时候Python解释器会从前往后顺序查找, 把找到的第一个
    匹配的结果进行加载.


    理解 “命名空间”

    名字冲突问题: 如果一个项目中的代码量很大, 那么很可能函数/类的名字会冲突.

    C语言采用的方案是,给函数名加一个又丑又长的前缀

    void Huichuan_AdServer_Strategy_IndexAccessor_Load()
    

    C++中使用命名空间来解决这个问题

    namespace Huichuan {
        namespace AdServer {
            namespace Strategy {
                namespace IndexAccessor {
                    void Load()
                }
            }
        }
    }
    

    Python的模块也相当于是命名空间. 例如 os.path.exists , 通过这样的层级结构, 将函数的名字组织起来, 就可以有效的避免命名冲突.


    import语句

    import语句导入模块, 可以一次import一个, 或者一次import多个->用,分割

    #方式1:
    import os
    import sys
    
    #方式2:
    import os,sys #用逗号分割
    

    推荐使用第一种方式, 这样代码看起来更好看一些


    import语句的顺序: 我们推荐按 Python标准库, Python第三方库, 应用程序自定制模块 的顺序来import,
    来提高我们代码的可读性.

    模块也是一个对象, 也有作用域这样的概念. 在函数内部import, 那么这个模块的作用域就是在这个函
    数内.

    例子:

    def func():
        import sys
        print('In func',sys.argv)	#In func ['E:/Python文件存储/Class01.py']
    func()
    print('Out func',sys.argv)	#报错 NameError: name 'sys' is not defined
    

    既然模块也是一个对象, 那么也可以给这个对象赋值(相当于定义别名).

    import os.path
    p = os.path #起别名
    print(p.exists('add.py'))   #add.py这个文件是否存在  True
    

    这样可以一定程度上简化我们的代码(敲一个字符p肯定比敲一串字符os.path要方便).


    import as语句

    前面我们使用赋值的方式, 给模块起了一个很短的别名. 实际上使用import-as可以更方便的完成这个动作

    import os.path as p
    print(p.exists('add.py'))   #add.py这个文件是否存在  True
    

    from-import 语句

    import语句是直接导入一个模块. 而有时候我们只需要用到模块中的某一个或几个函数, 就可以使用from-
    import

    from-import相当于把模块中的名字引入了当前文件的命名空间中.可以直接使用导入的函数

    from os.path import exists
    print(exists('add.py')) #True
    

    可以使用 from module import * 的方式将module模块中的所有名字都导入进来. 不推荐这么用.

    *:通配符


    导入模块意味着 “被执行”

    模块导入意味着这个模块 “被执行” , 也就是说所有顶层的代码(无缩进部分的代码)都会被执行到. 这通常包括函数的定义和全局变量的定义.

    例子:

    add.py 的内容:

    '''
    定义两数相加的函数
    '''
    def _Add(x,y):
        return x+y
    
    print("In Add函数内部")
    

    test.py的内容

    import add
    #执行结果:
    In Add函数内部
    

    这往往不是我们期望的结果(比如导入模块是打印了一些奇怪的日志), 我们只是想使用模块中的一些函数和变量. 因此往往我们在实现一个模块时, 只将函数定义/类定义放到顶层代码中.


    理解 “导入” 和 “加载”

    我们写 import module 时, 其实有两个大的阶段, 先将这个模块的文件加载到内存中, 并创建一个对象来表示; 然后通过一个变量名来将这个模块引入到当前的命名空间中.

    如果同一个模块(例如sys这样的常用模块)被import了多次, 其实加载动作只进行了一次(也就是说内存中只有一份sys的代码), 执行动作也只进行了一次

    import add
    import add
    #执行结果:
    In Add函数内部
    

    模块的内置变量

    我们可以通过globals()函数看到全局命名空间下有几个内置变量

    print(globals())
    #执行结果:
    {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000026C824D6400>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/Python文件存储/Class01.py', '__cached__': None}
    

    其中:

    _builtins_ : 内建函数模块. 包含了open, help, doc等内建函数的定义
    __name__ : **模块名字. 如果是从这个模块开始执行, 那么 __name__ 值为 __main__,否则就是当前文件不包含后缀的名称

    入口文件是谁,__name__就是__name__

    __file__ : 模块的文件名
    __doc__ : 模块的文档字符串
    __package__ :模块从属的包名


    回忆我们刚刚讲过的, import的时候, 会执行这个模块. 我们期望, 这个模块被别人导入时, 只进行函数定义; 如果直接从这个模块开始运行, 则在函数定义完成后再进行一些测试

    例子:

    '''
    定义两数相加的函数
    '''
    def Add(x,y):
        return x+y
    
    #测试:
    if __name__ == '__main__':
        print(Add(1,2))
        print(Add(2,3))
    

    判定 __name__ 是否值为 _main_ , 如果是的话, 说明是直接执行add.py, 这时候就执行测试代码. 否则
    认为add是被其他Python文件import的, 不执行测试代码.

    包(Package)

    当我们代码量进一步变大时, 光拆分成多个文件已经不足以满足需求, 还需要能按照一定的目录结构层次化的组织这些模块. 同时包也可以解决模块之间的名字冲突问题.

    目录也是包的一种 ,包也是模块

    如:以下面的方式组织代码结构:

    test.py
    calc/
    	add.py
        __init__.py
    

    在calc目录中增加一个 _init_.py 文件, calc这个目录就成了包(Package). 可以在test.py文件中import calc中的模块了

    **_init.py_**是包的初始化文件, 会首先执行它


    __init__.py 是在包加载的时候会进行执行, 负责一些包的初始化操作. 一般是空文件即可.


    包中是可以嵌套其他的包的

    来源:芒果再努力

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【Python】模块和包

    发表评论