Python模块化

1、模块化

一般来说,编程语言中,库、包、模块是一种概念,是代码组织方式。

Python中只有一种模块对象, 但是为了模块化组织模块的便利,提供了一个概念--包模块module,指的是Python的源代码文件。

包package,指的是模块组织在一起和包名同名的目录及其相关文件。

2、导入语句

语句

含义

Import模块1[模块2]

完全导入

Import...as.....

模块别名

Import 的作用:将需要的模块的名称引用到当前所有的模块的名词空间中。

加载到了sys.modules里面去了。

from (后面是模块)import(类、函数)

from pathlib import *

from子句中指定的模块,加载并初始化,(并不是导入)。

Import语句:

(1)找到指定的模块,加载并初始化他,生成模块对象。找不到,抛出importError异常。

(2)Import所在的作用域的局部命名空间内,增加名称和上一步创建的对象关联。

import functools
print(dir())
print(functools)
print(functools.wraps)

1,['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'functools']

2,<module 'functools' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\functools.py'>

3,<function wraps at 0x00000018C691F620>

import os.path
print(1,dir())
print(2,os)
print(3,os.path)

1 ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'os']

2 <module 'os' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\os.py'>

3 <module 'ntpath' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\ntpath.py'>

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

1 ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'osp']

2 <module 'ntpath' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\ntpath.py'>

总结:

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

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

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

语句

含义

from...import..

部分导入

From...import...as....

别名

From语句:

from pathlib import Path,PosixPath   #在当前名词空间指定导入该模块的指定成员
print(dir())

['Path', 'PosixPath', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

from pathlib import * #在当前名词空间导入该模块所有公共成员(非下划线开头)
print(dir())

['Path', 'PosixPath', 'PurePath', 'PurePosixPath', 'PureWindowsPath', 'WindowsPath', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

from functools import wraps as wr,partial
print(dir())

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'partial', 'wr']

from os.path import exists #加载、初始化os、os.path模块,exists加入到本地名词空间并绑定

if exists('c:/t'):
    print('yes')
else:
    print('no')
print(dir())
print(exists)

no

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'exists']

<function exists at 0x000000212F72B268>


import os

print(os.path.exists)
print(exists)
print(os.path.__dict__['exists'])
print(getattr(os.path,'exists'))

通过上面四种方式获得同一个对象。

总结:

找到from子句中指定的模块,加载并初始化他(不是导入)。

对于import子句后面的名称。

先查from子句导入的模块是否具有该名称的属性。

如果不是,则尝试导入该名称的子模块。

还没找到,则抛出importError异常

这个名称保存到本地名词空间中,如果有as子句,则使用as子句后面的名称。

from pathlib import Path
print(1,Path,id(Path))

import pathlib as p1
print(2,dir())
print(3,p1)
print(4,p1.Path,id(p1.Path))

1 <class 'pathlib.Path'> 784869820392

2 ['Path', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'p1']

3 <module 'pathlib' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\pathlib.py'>

4 <class 'pathlib.Path'> 784869820392

看出导入的Path和p1.Path是同一个对象。

3、自定义模块

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

自定义模块名命名规范:

(1)模块名就是文件名

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

(3)不能使用系统模块名来避免冲突,除非知道模块名的用途。

(4)模块名全为小写,下划线来分隔。

4、模块搜索顺序

import sys

for i in sys.path:
    print(i)

C:\Users\WCL\PycharmProjects\untitled1\package\exercise

C:\Users\WCL\PycharmProjects\untitled1

C:\Users\WCL\PycharmProjects\untitled1\venv\Scripts\python35.zip

C:\Users\WCL\AppData\Local\Programs\Python\Python35\DLLs

C:\Users\WCL\AppData\Local\Programs\Python\Python35\lib

C:\Users\WCL\AppData\Local\Programs\Python\Python35

C:\Users\WCL\PycharmProjects\untitled1\venv

C:\Users\WCL\PycharmProjects\untitled1\venv\lib\site-packages

C:\Users\WCL\PycharmProjects\untitled1\venv\lib\site-packages\setuptools-28.8.0-py3.5.egg

C:\Users\WCL\PycharmProjects\untitled1\venv\lib\site-packages\pip-9.0.1-py3.5.egg

使用sys.pah查看搜索顺序。

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

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

.egg文件,由setuptools库创建的包,第三方库常见的格式,添加了元数据,版本号等,依赖项。

信息的zip文件。

路径顺序为:程序的主目录,程序运行的主程序脚本所在的目录,Pythonpath目录,环境变量PYTHONPATH设置额目录也是搜索模块的路径。

标准库目录,Python自带的库模块所在目录。

Sys.path可以被修改,追加新的目录。

5、模块的重复导入

模块并不会重复导入,就是查字典的过程(从前向后找。)所有加载的模块都会记录在sys.modules中,sys.modules是存储已经加载过的所有模块的字典。

6、模块运行

__name__        每个模块都会定义一个__name__特殊变量来存储当前模块的名称,如果不指定,则默认为源代码文件名,如果是包则有限定名。__name__这个属性,用来定义当前的文件名称。

解释器初始化的时候,会初始化sys.modules字典,(保存已加载的模块),创建buildtins(全局函数、常量),模块,__main__、sys模块,以及模块搜索路径sys.path.

搜索路径也要加到sys.path中来。

python是脚本语言,任何一个脚本都可以直接执行,也可以作为模块被导入。

当标准输入、脚本或交互式读取的时候,会将模块的 __name__,设置为__main__,模块的顶层代码就在__main__这个作用域中执行,顶层代码:模块中缩进外层的代码。

如果是import导入的,其__name__默认就是模块名。

从哪里运行就把__name__ 改为__main__

7、If __name__ == ‘__        main__   ‘:用途

1)本模块的功能测试。

对于非主模块,测试本模块内的函数,类。

2)避免主模块变更的副作用。

顶层代码,没有封装,主模块使用时候没有问题,但是一旦有了新的主模块,老的主模块成了被导入模块,由于原来的代码没有封装,一并执行

Sys.path(搜索路径)

Sys.moduels     (字典,已加载的在里面。)

if __name__ == '__main__':
    print('in __main__')
else:
    print('in import module')

in __main__

import m2

in import module

8、模块的属性

属性

含义

__file__

字符串,源文件路径

__cached__

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

__spec__

显示模块的规范

__name__

模块名

__package__

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

9、包

包:特殊的模块。

Python支持目录。

项目中新建一个目录m

Import m

print(m)

Print(type(m))

Print(dir())#  没有__file__属性

可以导入目录m,m也是文件,可以导入,目录模块写入代码,在其目录下简历一个特殊的文件__init__.py,在其中写入代码。

Pycharm 中创建普通的文件夹和Python的包不同,普通包只是创建目录,后面的则是创建一个带有_init__.py文件的目录即包。

10、子模块

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

如上建立子模块目录和文件,所有的py文件中就写一句话print(__name__)

Import * 只是拿共有的模块的内容。公共成员,私有的不能拿。

Import m (加载m)

Import m.m1(加载m和m1)

Import m.m2.m21(加载m,m2,m21)

From m import m1 从m中加载m1

From m.m2 import m21 三层加载。

保留__init__文件。

11、模块和包的总结

包能够更好的组织模块,由其是在大的规模下代码行数很多,可以将其拆分为很多子模块,便于使用某些功能就在家相应的子模块。

包目录中 __init__.py 是包在导入的时候就会执行,内容可以为空,也可以用于该报初始化工作的代码。

导入子模块一定会导入父模块,导入父模块就不会加载子模块。

包目录之间只能使用.点号作为间隔符,表示模块及其子模块之间的层级关系。       

模块也是封装,如同类,函数,不过其能够封装变量、类、函数。

模块就会命名空间,其内部的顶层标识符,都是其属性,可以通过__dict__或者dir()查看。

包也是模块,但是模块不一定是包,包是特殊的模块,是一种组织方式,包含__path__属性。

From ison import encoder:   不可以执行ison.dump

Import json.encoder:  可以执行json.dump

12、绝对导入和相对导入

绝对导入:

在import 语句或者from导入模块,模块名称前不是以.开头的,绝对导入总是去搜索模块搜索路径中找。

相对导入:      

包内使用相对的路径。且只能用在from语句中,不在包外用。在顶层用绝对导入。

使用 ...上一级的上一级 ..上一级 .同一级。不在顶层使用相对导入。

对于包来说正确的使用方式还是在顶级模块中使用这些包。

13、访问控制

下划线或者双下划线开头的模块能够被导入,都可以成功的导入,因为他们都是合法的标识符,都可以用作模块名。

模块内的标识符:

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

使用from可以访问所有变量。

14、from ... import * 和__all__

使用from ... import *导入。起到作用的是from m import *。所有共有成员非子成员的全部导入。会影响当前的名词空间。

定义了__all__  使用all指定导入的是哪些。

使用__all__是一个列表,元素是字符串,每个元素都是一个模块的名称。(变量名)

15、包和子模块

如何访问到一个模块中的一个变量呢:

1)直接导入整个模块  import

2)直接导入from 模块中import 需要的属性。

3)利用from 模块import* 利用__all__指定需要导入的名称。

4)在__init__.py中增加from . import 模块。

16、总结

一、使用from xyz import *导入

(1)如果模块没有__all__,from xyz import * 只是导入非下划线开头的模块的变量,如果是包,子模块不会导入,除非在__all__中设置,或者使用__init__.py文件中使用相对导入。

(2)如果模块有__all__,from xyz import * 只导入__all__列表中指定的名称,哪怕是这个名词是下划线开头或者子模块。

(3)From xyz import * 方式带入,使用简单,但是其副作用是导入大量不需要的变量,甚至造成名字的冲突,而__all__可控制被导入模块在这种导入方式下能够提供的变量名称,就是为了阻止from xyz import *导入过多的模块变量,从而避免冲突,因此,编写模块时候,尽量加入__all__

二、From module import name1,name2导入

导入时明确的,导入子模块,或者导入下划线开头的名称,可以有控制的导入名和其对应的对象。

17、模块变量的修改

模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改,会影响所有者,除非万不得已,不要修改模块的变量。

猴子补丁,也可以通过打补丁的方式,修改模块的变量,类和函数等内容。

猜你喜欢

转载自www.cnblogs.com/wangchunli-blogs/p/9949872.html