模块化

一般来说。编程语言中,库、包、模块是一个概念,是代码组织方式。
Python中只有一种模块对象类型,但是为了模块化组织模块便利,提供了包的概念。
模块module,指的是Python的源代码文件
包package,指的是模块组织在一起的和包名同名的目录及其相关文件。

导入语句

语句

含义

import 模块1[,模块2,……]

完全导入

import……as……

模块别名

import语句

  • 1、找到指定的模块,加载和初始化它,生成模块对象。找不到,抛出异常
  • 2、在import所在的作用域的局部命名空间中,增加名称和上一步创建的对象关联
import  functools
print(dir())
print(functools)
print(functools.wraps)

python模块化程序设计例子 python怎么模块化_json

import os
print(dir())

python模块化程序设计例子 python怎么模块化_json_02

import os.path
print(dir())

python模块化程序设计例子 python怎么模块化_加载_03


这里分别引用了os和os.path,但是全局属性都是只有os,表明无论引用如何,全局属性都只有最全的那一层,也就是模块那一层

import os.path
print(dir())
print(os)
print(os.path)

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_04

import os.path as osp
print(dir())
print(osp)

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_05

def fn():
    import os.path
    print(dir())
fn()
print(globals().keys())

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_06


总结:

导入顶级模块,其名称会加入到本地名词空间中,并绑定到其模块对象

导入非顶级模块,只将其顶级模块名称加入到本地名词空间中,导入的模块必须使用完全限定名称来访问。

如果使用了as,as后的名称直接绑定到导入的模块对象,并将该名称加入到本地名词空间中。

语句

含义

from……import……

部分导入

from……import……as……

别名

总结:

  • 找到from子句中指定的模块,加载并初始化它
  • 对于import子句后的名称
  • 先查from子句导入的模块是否具有该名称的属性
  • 如果不是,则尝试导入该名称的子模块
  • 如果还没有,则抛出importError异常
  • 这个名称保存在本地名词空间中,如果有as子句,则使用as子句后的名称。
from pathlib import Path
print(Path,id(Path))
import  pathlib as p1
print(dir())
print(p1)
print(p1.Path,id(p1.Path))

python模块化程序设计例子 python怎么模块化_搜索_07

自定义模块

自定义模块:.py文件就是一个模块

#base.py
print('This is test1.py')
class A:
    def show(self):
        print(1,self.__module__,self)
        print(2,self.__dict__)
        print(3,self.__class__.__dict__)
        print(4,self.__class__.__name__)
a=A()
a.show()


#test2.py
import base
a=base.A()
a.show()

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_08


自定义模块命名规范

1、模块名就是文件名

2、模块名必须符合标识符的要求,是非数字开头的字母、数字和下划线的组合。

3、不要使用系统模块名来避免冲突,除非明确要使用模块名的用途

4、通常模块名全为小写,下划线来分割。

模块搜索顺序

使用sys.path查看搜索顺序

import sys
print(*sys.path,sep='\n')

python模块化程序设计例子 python怎么模块化_搜索_09


显示结果为,Python模块的路径搜索顺序

当加载一个模块的时候,需要从这些搜索路径中从前到后依次查找,并不搜索这些路径的子目录。

搜索到模块就加载,搜索不到就抛异常。

路径也可以为字典、zip文件、egg文件。

.egg文件是由setuptools库创建的包,第三方库常用的格式,添加了元数据(版本号,依赖项等)信息的zip文件

路径顺序为:

  • 1、程序主目录,程序运行的主程序脚本所在的目录
  • 2、PYTHONPATH目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径。
  • 3、标准库目录,Python自带的库模块所在目录
模块的重复导入
#base.py
print('This is test1.py')
class A:
    def show(self):
        print(1,self.__module__,self)
        print(2,self.__dict__)
        print(3,self.__class__.__dict__)
        print(4,self.__class__.__name__)
a=A()
a.show()

import base
print('~~~~~~~~~')
import  base
import  base
import  base

python模块化程序设计例子 python怎么模块化_搜索_10


从运行结果来看,不会出现重复导入的现象

所有加载的模块都会记录在sys.modules中,sys.modules是存储已经加载过的所有模块的字典。

打印sys.modules可以看到os、os.path都已经加载了。

模块运行

__name__每个模块都会定义一个__name__特殊变量来存储当前模块的名称,如果不指定,则默认为源代码文件名,如果是包则有限定名。
解释器初始化的时候,会初始化sys.modules字典(保存已加载的模块),加载builtins(全局函数、常量)模块、main__模块、sys模块、以及初始化模块搜索路径sys.path
Python是脚本语言,任何一个脚本都可以直接执行,也可以作为模块被导入。
当从标准输入(命令行方式敲代码)、脚本($Python test.py)或交互式读取的时候,会将模块的__name__设置为__main,模块的顶层代码就在__main__这个作用域中执行。顶层待嘛:模块中缩进最外层的代码。如果是import导入的,其__name__默认就是模块名

#test1.py文件
import test2
#test2.py文件
#判断模块是否以程序的方式运行$python test.py
if __name__=='__main__':
    print('in __main__')
else:
    print('in imported module')

python模块化程序设计例子 python怎么模块化_json_11


if name==‘main’:的用途

1、本模式的功能测试,对于非主模块,测试本模块内的函数、类

2、避免主模块变更的副作用,顶层代码,没有封装,主模块使用时没有问题。但是,一旦有了新的主模块,老的主模块成了被导入模块,由于原来代码没有封装,一并执行了。

模块的属性

属性

含义

file

字符串,源文件路径

cached

字符串,编译后的字节码文件路径

spec

显示模块的规范

name

模块名

package

当模块是包,同__name__,否则,可以设置为顶级模块的空字符串

Python模块支持模块
在项目中新建目录m,使用下面的代码

import m
print(m)
print(type(m))
print(dir(m))

pycharm中,创建Directory和创建python package不同,前者时创建普通的目录,后面是创建一个带有__init__.py文件的目录即包。
Python中,目录可以作为模块,这就是包,不过代码需要写在该目录下__init__.py中。

子模块

包目录下的py文件、子目录都是其子模块。

python模块化程序设计例子 python怎么模块化_json_12


如上建立子模块目录和文件,所以的py文件中就写一句话

print(__name__)
#注意观察已经加载的模块,当前名词空间的名词
#import m
# import m.m1
# from m import m1
#from m.m2 import m21
import m.m2.m21
print(dir())
print('~~~~~~~')
import sys
print(sorted(filter(lambda x: x.startswith('m'),sys.modules.keys())))

python模块化程序设计例子 python怎么模块化_加载_13


删除__init__.py后发下并不影响导入,但建议保留。

模块和包的总结

包能够更好的组织模块,尤其是大的模块,其代码行数最多,可以把它分解成很多的子模块,便于使用某些功能就加载相应的子模块。
包目录中的__init__.py是在包第一次导入的时候就会执行,内容可以为空,也可以是用于该包初始化工作的代码。
最好不要删除__init__.py文件
导入子模块一定会加载父模块,但是导入父模块一定不会导入子模块。
包目录之间只能使用.点号作为间隔符,表示模块及其子模块的层级关系。
模块也是封装,如同类、函数,不过它能够封装变量、类、函数
模块就是命名空间,其内部的顶级标识符,都是它的属性,可以通过__dict__或者dir(module)查看。
包也是模块,但模块不一定是包,包是特殊的模块,它包含有__path__属性
问题

  • from json import encoder之后,json.dump函数用不了?
  • 原因是from json import encoder之后,当前名词空间没有Json,json模块已经加载过了,没有json的引用,无法使用dump函数。
  • import json.encoder之后,json.dump函数是否能够使用?
  • import json.encoder也加载json模块,但是当前名词空间有json,因此可以调用json.dump。
绝对导入、相对导入
  • 绝对导入
    在import语句或者from导入模块,模块名称最前面不是以.开头的
    绝对导入总是去模块搜索中找,当前会查看一下该模块是否已经加载。
  • 相对导入
    只在包内使用,且只用在from语句中。
    使用.点号,表示当前目录内
    ..表示上一级目录
    不要在顶级模块中使用相对导入
访问控制

下划线开头的模块名
_或者__开头的模块都能够被导入

#xyz.py
print(__name__)
A=5
_B=6
__C=7
__my__=8
#other.py
import sys
import xyz
print(sorted(sys.modules.keys()))
print(dir())
print(xyz.A,xyz._B,xyz.__C,xyz.__my__)

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_14


可以看出普通变量、保护变量、私有变量、特殊变量都没有被隐藏,也就是说模块内没有私有的变量,在模块中定义不做特殊处理。

from xyz import A,_B,__C,__my__
import sys
print(sorted(sys.modules.keys()))
print(dir())
print(A,_B,__C,__my__)

python模块化程序设计例子 python怎么模块化_加载_15

from …import * 和__all__

先使用from…import*导入

#xyz.py
print(__name__)
A=5
_B=6
__C=7
__my__=8
#other.py
import sys
from xyz import *
print(sorted(sys.modules.keys()))
print(dir())
print(locals()['A'])
A=55
print(locals()['A'])

python模块化程序设计例子 python怎么模块化_搜索_16


结果只导入了A,下滑线开头的都没有导入

使用__all__

__all__是一个列表,元素是字符串,每一个元素都是一个模块内的变量名

#xyz.py
print(__name__)
A=5
_B=6
__C=7
__my__=8
__all__=['A','_B']
#other.py
import sys
from xyz import *
print(sorted(sys.modules.keys()))
print(dir())
print(locals()['A'])
A=55
print(locals()['A'])

python模块化程序设计例子 python怎么模块化_加载_17


可以使用from xyz import *导入__all__列表中的名称

包和子模块

python模块化程序设计例子 python怎么模块化_json_18

#__init__.py中
print(__name__)
x=1
#b.py中
print(__name__)
y=5

如何在另一个模块中访问到a.b.y

#方法一,直接导入b模块
import a.b
print(a.b.y)

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_19

#方法二,直接导入属性y
from a.b import y
print(y)

python模块化程序设计例子 python怎么模块化_加载_20

#方法三,from a import *
from a import *
print(b.y)

python模块化程序设计例子 python怎么模块化_json_21

#方法四,在__init__.py中添加from . import b
from a import *
print(b.y)

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_22


init.py中有什么变量,则使用from m import *加载什么变量,这依然符合模块的访问控制。

总结

使用 from xyz import *导入

  • 如果模块没有__all__,from xyz import *只导入非下划线开头的该模块的变量,如果是包,子模块也不会导入,除非__all__中设置,或__init__.py中导入它们
  • 如果模块有__all__,from xyz import *只导入__all__列表中指定的名称,哪怕这个名词是下划线开头的,或者是子模块
  • from xyz import * 方式导入,使用简单,但是其副作用是导入大量不需要使用的变量,甚至有可能造成名称的冲突。而__all__可以控制被导入模块在这种导入方式下能够提供的变量名称,就是为了阻止from xyz import *导入过多的模块变量,从而避免冲突。因此编写模块尽量加入__all__
模块变量的修改
import xyz
print(xyz.x)
xyz.x=100
print('~~~~~~~~~~~~~')
print(xyz.x)

python模块化程序设计例子 python怎么模块化_python模块化程序设计例子_23


模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改,会影响所有使用者。

尽量不要修改模块的变量。