一、什么是模块?python
一个模块就是一个Python文件,文件名就是模块名字加上.py后缀。所以模块名称也必须符合变量名的命名规范。mysql
1 使用python编写的代码(.py文件)sql
2 已被编译为共享库或DLL的C或C++扩展django
3 包好一组模块的包json
4 使用C编写并连接到python解释器的内置模块api
二、为何要使用模块?app
若是你退出python解释器而后从新进入,那么你以前定义的函数或者变量都将丢失,所以咱们一般将程序写到文件中以便永久保存下来,须要时就经过python test.py方式去执行,此时test.py被称为脚本script。ide
随着程序的发展,功能愈来愈多,为了方便管理,咱们一般将程序分红一个个的文件,这样作程序的结构更清晰,方便管理。这时咱们不只仅能够把这些文件当作脚本去执行,还能够把他们当作模块来导入到其余的模块中,实现了功能的重复利用,函数
三、如何使用模块?测试
首先,自定义一个模块my_module.py,文件名my_module.py,模块名my_module
name = "我是自定义模块的内容..." def func(): print("my_module: ", name) print("模块中打印的内容...")
在import一个模块的过程当中,发生了哪些事情?
# 用import导入my_module模块 import my_module >>> 模块中打印的内容... # 怎么回事,居然执行了my_module模块中的print语句 import my_module import my_module import my_module import my_module import my_module >>> 模块中打印的内容... # 只打印一次
从上面的结果能够看出,import一个模块的时候至关于执行了这个模块,并且一个模块是不会重复被导入的,只会导入一次(python解释器第一次就把模块名加载到内存中,以后的import都只是在对应的内存空间中寻找。)成功导入一个模块后,被导入模块与文本之间的命名空间的问题,就成为接下来要搞清楚的概念了。
被导入模块与本文件之间命名空间的关系?
假设当前文件也有一个变量为: name = 'local file', 也有一个同名的func方法。
# 本地文件 name = "local file" def func(): print(name) # 本地文件有跟被导入模块同名的变量和函数,究竟用到的是哪一个呢? import my_module print(my_module.name) # 根据结果能够看出,引用的是模块里面的name my_module.func() # 执行的是模块里面的func()函数 >>> 模块中打印的内容... 我是自定义模块的内容... my_module: 我是自定义模块的内容... print(name) # 使用的是本地的name变量 func() # 使用的是本地的func函数 >>> local file local file
在import模块的时候发生了下面的几步:
一、先寻找模块
二、若是找到了,就在内存中开辟一块空间,从上至下执行这个模块
三、把这个模块中用到的对象都收录到新开辟的内存空间中
四、给这个内存空间建立一个变量指向这个空间,用来引用其内容。
总之,模块与文件之间的内存空间始终是隔离的
给导入的模块取别名,用as关键字
若是导入的模块名太长很差记,那么能够经过“import 模块名 as 别名”的方式给模块名取一个别名,但此时原来的模块就再也不生效了(至关于建立了新的变量名指向模块内存空间,断掉原模块名的引用)。
# 给my_module模块取别名 import my_module as sm print(sm.name) >>> 我是自定义模块的内容... print(my_module.name) # 取了别名后,原来的模块名就不生效了 >>> NameError: name 'my_module' is not defined
给模块去别名,还可使代码更加灵活,减小冗余,经常使用在根据用户输入的不一样,调用不一样的模块。
# 按照先前的作法,写一个函数,根据用户传入的序列化模块,使用对应的方法 def dump(method): if method == 'json': import json with open('dump.txt', 'wb') as f: json.dump('xxx', f) elif method == 'pickle': import pickle with open('dump.txt', 'wb') as f: pickle.dump('xxx', f) # 上面的代码冗余度很高,若是简化代码?经过模块取别名的方式,能够减小冗余 def dump(method): if method == 'json': import json as m elif method == 'pickle': import pickle as m with open('dump.txt', 'wb') as f: m.dump('dump.txt', f)
如何同时导入多个模块?
方式一:每行导入一个模块
import os import sys import time
方式二:一行导入多个模块,模块之间经过逗号“,”来分隔
import os, sys, my_module
可是,根据PEP8规范规定使用第一种方式,而且三种模块有前后顺序(内置>第三方>自定义)
# 根据PEP8规范 import os import django import my_module
模块搜索路径
经过sys内置模块,咱们知道sys.path存储了全部模块的路径,可是正常的sys.path的路径中除了内置模块,第三方模块所在的路径以外,只有一个路径是永远正确的,就是当前执行的文件所在目录。一个模块是否可以被导入,就取决于这个模块所在的目录是否在sys.path中。
python解释器在启动时会自动加载一些模块,可使用sys.modules查看
在第一次导入某个模块时(好比my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),若是有则直接引用
若是没有,解释器则会查找同名的内建模块,若是尚未找到就从sys.path给出的目录列表中依次寻找my_module.py文件。
因此总结模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
须要特别注意的是:咱们自定义的模块名不该该与系统内置模块重名。
模块和脚本
运行一个py文件有两种方式,可是这两种执行方式之间有一个明显的差异,就是__name__。
一、已脚本的方式执行:cmd中“python xxx.py” 或者pycharm等IDE中执行
__name__ = '__main__'
二、导入模块时执行:import模块,会执行该模块。
__name__ = 模块名
然而,当你有一个py文件既能够做为脚本执行,又能够做为模块提供给其余模块引用时,这时做为模块须要导入时而不显示多余的打印逻辑/函数调用,因此这些逻辑能够放在“if __name__ = '__main__': xxx” 代码块中。
这样py文件做为脚本执行的时候就可以打印出来,以模块被导入时,便不会打印出来。
from...import是另外一种导入模块的形式,若是你不想每次调用模块的对象都加上模块名,就可使用这种方式。
在from ... import ... 的过程当中发生了什么事儿?
from my_module import name, func print(name) # 此时引用模块中的对象时,就不要再加上模块名了。 func()
一、寻找模块
二、若是找到模块,在内存中开辟一块内存空间,从上至下执行模块
三、把模块中的对应关系所有都保存到新开辟的内存空间中
四、创建一个变量xxx引用改模块空间中对应的xxx, 若是没有import进来的时候,就使用不了。
from ... import ... 方式取别名
与import方式一模一样,经过"from 模块名 import 对象名 as 别名"。
from my_module import name as n, func as f
from ... import *
import * 至关于把这个模块中的全部名字都引入到当前文件中,可是若是你本身的py文件若是有重名的变量,那么就会产生很差的影响,所以使用from...import *时须要谨慎,不建议使用。
* 与 __all__
__all__是与*配合使用的,在被导入模块中增长一行__all__=['xxx','yyy'],就规定了使用import *是只能导入在__all__中规定的属性。
# 在my_module模块中定义__all__ __all__ = ['name'] name = 'My module...' def func(): print("my_module: ", name) # 在其余文件中经过import *导入全部属性 from my_module import * print(name) >>> My module... func() >>> NameError: name 'func' is not defined
拓展知识点:
(1)pyc文件与pyi文件 *
pyi文件:跟.py同样,仅仅做为一个python文件的后缀名。
pyc文件: python解释器为了提升加载模块的速度,会在__pycache__目录中生成模块编译好的字节码文件,而且对比修改时间,只有模块改变了,才会再次编译。pyc文件仅仅用于节省了启动时间,可是并不能提升程序的执行效率。
(2)模块的导入和修改 *
1.导入模块后,模块就已经被加载到内存中,此后计算对模块进行改动,读取的内容仍是内存中原来的结果。
2.若是想让改动生效,能够经过“from importlib import reload”, 须要'reload 模块名'从新加载模块,改动才生效。
(3)模块的循环使用 ****
谨记模块的导入必须是单链的,不能有循环引用,若是存在循环,那么就是程序设计存在问题。
(4)dir(模块名) ***
能够得到该模块中全部的名字,并且是字符串类型的,就能够经过反射去执行它。
包是一种经过‘.模块名’来组织python模块名称空间的方式。
(1)不管是import形式仍是from ... import 形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提升警觉:这是关于包才有的导入语法
(2)包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)
(3)import导入文件时,产生名称空间中的名字来源与文件,import包,产生的名称空间的名字一样来源与文件,即包下的__init__.py,导入包本质就是在导入文件
注意:
一、在python3中,即便包下没有__init__.py文件,import包仍然不会报错,而在python2中,包下必定要有该文件,不然import包会报错
二、建立包的目的不是为了运行,而是被导入使用,记住,包只有模块的一种形式而已,包即模块
包A和包B下有同名模块也不会冲突,如A.a与B.a来自两个命令空间
示例环境以下:
import os
os.makedirs('glance/api')
os.makedirs('glance/cmd')
os.makedirs('glance/db')
l = []
l.append(open('glance/__init__.py','w'))
l.append(open('glance/api/__init__.py','w'))
l.append(open('glance/api/policy.py','w'))
l.append(open('glance/api/versions.py','w'))
l.append(open('glance/cmd/__init__.py','w'))
l.append(open('glance/cmd/manage.py','w'))
l.append(open('glance/db/models.py','w'))
map(lambda f:f.close() ,l)
glance/ #Top-level package
├── __init__.py #Initialize the glance package
├── api #Subpackage for api
│ ├── __init__.py
│ ├── policy.py
│ └── versions.py
├── cmd #Subpackage for cmd
│ ├── __init__.py
│ └── manage.py
└── db #Subpackage for db
│ ├── __init__.py
│ └── models.py
#文件内容
#policy.py
def get():
print('from policy.py')
#versions.py
def create_resource(conf):
print('from version.py: ',conf)
#manage.py
def main():
print('from manage.py')
#models.py
def register_models(engine):
print('from models.py: ',engine)
(1)从包中导入模块有两种方式,可是不管哪一种,不管在什么位置,都必须遵循一个原则:(凡是在导入时带点的,点的左边都必须是一个包),不然非法。
(2)对于导入后,在使用就没有这种限制,点的左边能够是包,模块,函数,类(它们均可以用点的方式调用本身的属性)
(3)对比import item 和from item import name的应用场景:若是咱们想直接使用name那么必须使用后者。
方式一:import
例如: 包名1.包名2.包名3.模块名
# 在与包glance同级别的文件中测试 import glance.db.models glance.db.models.register_models('mysql') """执行结果:from models.py mysql"""
方式二:from ... import ...
例如:from 包名1.包名2 import 模块名
from 包名1.包名2.模块名 import 变量名/函数名/变量名
注意:须要注意的是from后import导入的模块,必须是明确的一个不能带点,不然会有语法错误,如:from a import b.c是错误语法
# 在与包glance同级别的文件中测试 from glance.db import models models.register_models('mysql') """执行结果:from models.py mysql""" from glance.cmd import manage manage.main() """执行结果:from manage.py"""
若是是直接导入一个包,那么至关于执行了这个包中的__init__文件
并不会帮你把这个包下面的其余包以及py文件自动的导入到内存
若是你但愿直接导入包以后,全部的这个包下面的其余包以及py文件都能直接经过包来调用,那么须要你本身处理__init__文件。
__init__.py文件
无论是哪一种方式,只要是第一次导入包或者是包的任何其余部分,都会依次执行包下的__init__.py文件;这个文件能够为空,可是也能够存放一些初始化包的代码。
绝对导入和相对导入
咱们的最顶级包glance是写给别人用的,而后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以glance做为起始
相对导入:用. 或者.. 的方式做为起始(只能在一个包中使用,不能用于不一样目录内)
绝对导入和绝对导入示例:
绝对导入: 既然导入包就是执行包下的__init__.py文件,那么尝试在啊glance的__init__.py文件中"import api",执行一下,貌似没有报错,在尝试下在包外导入,状况如何? 在包外建立一个test.py文件,在里面操做以下: import glance glance.api ModuleNotFoundError: No module named 'api' 缘由:为何还会报错?由于一个模块能不能被导入就看在sys.path中有没有路径,在哪里执行文件,sys.path永远记录该文件的目录。 (1)在glance的__init__.py文件中,sys.path的路径是: 'E:\\Python练习\\包\\glance' 因此可以找到同级的api (2)可是在test文件中导入,此时sys.path的路径是: 'E:\\李彦杰\\Python练习\\包' 因此找不到不一样层级的api,因此就会报No module name 'api' 解决办法一: 使用绝对路径(绝对路径为当前执行文件的目录) (1)在glance包中的__init__.py中经过绝对路径导入: "from glance import api" (2)这样在test文件中执行,就能找到同层级的glance,再去里面找api (3)同理,若是想使用api包中的模块,也要在api包中的__init__.py文件中导入"from glance.api import policy, veersions", (4)如今在test文件中调用glance下的api下的policy模块就不会报错: import glance glance.api.policy.get() glance.api.versions.create_resource('测试') 执行结果: from policy.py from versions.py 测试 绝对导入的缺点: 若是之后包的路径发生了转移,包内的全部__init__.py文件中的绝对路径都须要改变 解决办法二: 使用相对导入 . 表示当前目录 .. 表示上一级目录 (1)在glance包中的__init__.py中经过相对路径的形式导入: “from . import api” (2)同理在api包中的__init__.py中经过相对路径的形式导入: “from . import policy,version” (3)一样在test文件中调用glance下的api下的policy模块就不会报错: import glance glance.api.policy.get() glance.api.versions.create_resource('测试') 执行结果: from policy.py from versions.py 测试 相对导入的优势: 包发生路径转移,其中的相对路径都没有改变,因此不用逐个逐个修改。 相对导入的缺点: 但凡带着相对导入的文件,只能当作模块导入,不能做为一个脚本单独执行!!!
扩展知识:
同级目录下的包导入
需求:如今须要在bin下面的start文件中导入core目录下的main模块;怎么破?
project ├── bin #Subpackage for bin ├── __init__.py └── start.py ├── core #Subpackage for core ├── __init__.py └── main.py
# main.py文件中的内容: def func(): print("In main")
(1)、在start中直接导入,由于路径不对,因此直接报错:
import main # 执行,报错ModuleNotFoundError: No module named 'main'
(2)、由上面报错咱们知道确定路径不对,那么咱们想到直接将core路径加进去不就行了吗?是的,这样是可行的
import sys path = 'E:\练习\包\core' # 复制获得core的绝对路径 sys.path.append(path) # 将core路径添加 import main # 再次导入便不会报错 main.func() # 执行结果:In main
(3)、上面的方法看似可行,可是仍是有一个问题,若是我将project打包发给别人,或者我换个环境运行呢? 那么又得更改对应的path。不怎么合理,那么咱们看下面的方法:
import sys print(__file__) ret = __file__.split('/') base_path = '/'.join(ret[:-2]) sys.path.append(base_path) from core import main main.func() # In main
一、__file__ 能够获得当前文件的绝对路径,E:/练习/project/bin/start.py
二、__file__.split('/') 将当前文件的绝对路径进行处理,按照'/'分隔获得:['E:', '练习', 'project', 'bin', 'start.py']
三、'/'.join(ret[:-2]) 由于咱们只须要拿到project项目的动态路径,因此进行切割,在jojn获得: E:/练习/project
四、sys.path.append(base_path) 再将获得的路径添加到sys.path中
五、from core import main 由于咱们拿到的是project目录,因此导入是从当前路径的core包导入main模块
六、main.func() 最后再是模块名.方法。