Python模块化
Posted wangchunli-blogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:UsersWCLPycharmProjectsuntitled1packageexercise
C:UsersWCLPycharmProjectsuntitled1
C:UsersWCLPycharmProjectsuntitled1venvScriptspython35.zip
C:UsersWCLAppDataLocalProgramsPythonPython35DLLs
C:UsersWCLAppDataLocalProgramsPythonPython35lib
C:UsersWCLAppDataLocalProgramsPythonPython35
C:UsersWCLPycharmProjectsuntitled1venv
C:UsersWCLPycharmProjectsuntitled1venvlibsite-packages
C:UsersWCLPycharmProjectsuntitled1venvlibsite-packagessetuptools-28.8.0-py3.5.egg
C:UsersWCLPycharmProjectsuntitled1venvlibsite-packagespip-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、模块变量的修改
模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改,会影响所有者,除非万不得已,不要修改模块的变量。
猴子补丁,也可以通过打补丁的方式,修改模块的变量,类和函数等内容。
以上是关于Python模块化的主要内容,如果未能解决你的问题,请参考以下文章
Python练习册 第 0013 题: 用 Python 写一个爬图片的程序,爬 这个链接里的日本妹子图片 :-),(http://tieba.baidu.com/p/2166231880)(代码片段