一、什么是模块
为了编写可维护的代码,把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,编程语言采用这种组织方式。在python中,一个.py文件就称之为一个模块。
二、使用模块有什么好处
- 最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。经常引用其他模块,包括Python内置的模块和来自第三方的模块
- 使用模块还可以避免函数名和变量名冲突。每个模块有独立的命名空间
三、模块分类
- 内置标准模块(又称标准库)执行help(‘modules’)查看所有python自带模块列表
- 第三方开源模块,可通过pip install 模块名联网安装
- 自定义模块
四、模块调用
import module from module import xx from module.xx.xx import xx as rename from module.xx.xx import *
注意:模块一旦被调用,即相当于执行了另外一个py文件里的代码
自定义模块
这个最简单, 创建一个.py文件,就可以称之为模块,就可以在另外一个程序里导入
模块查找路径
发现,自己写的模块只能在当前路径下的程序里才能导入,换一个目录再导入自己的模块就报错说找不到了, 这是为什么?
这与导入路径有关
import sys print(sys.path)
输出
['D:\Python\14_day_training_camp\Python全栈开发中级\第二模块\chapter4-常用模块', 'D:\Python\14_day_training_camp', 'D:\Python\14_day_training_camp\Python全栈开发中级\第二模块\chapter4-常用模块\my_proj', 'D:\Python\14_day_training_camp\作业\第五次作业\ATM', 'D:\Python\14_day_training_camp\Python全栈开发中级\ATM程序', 'D:\Python36\python36.zip', 'D:\Python36\DLLs', 'D:\Python36\lib', 'D:\Python36', 'D:\Python36\lib\site-packages', 'D:\Python36\lib\site-packages\openpyxl-2.3.3-py3.6.egg', 'D:\Python36\lib\site-packages\et_xmlfile-1.0.1-py3.6.egg', 'D:\Python36\lib\site-packages\jdcal-1.3-py3.6.egg', 'D:\Python36\lib\site-packages\file_magic-0.3.0-py3.6.egg', 'D:\Python36\lib\site-packages\win32', 'D:\Python36\lib\site-packages\win32\lib', 'D:\Python36\lib\site-packages\Pythonwin', 'D:\Program Files\JetBrains\PyCharm 2018.1.1\helpers\pycharm_matplotlib_backend']
python解释器会按照列表顺序去依次到每个目录下去匹配你要导入的模块名,只要在一个目录下匹配到了该模块名,就立刻导入,不再继续往后找。
注意列表第一个元素代表当前目录,所以你自己定义的模块在当前目录会被优先导入。
四、开源模块安装、使用
- pip3 install 模块名
- 在官网下载源码安装
编译源码 python setup.py build 安装源码 python setup.py install
五、包(package)
当模块文件越来越多,需要对文件按作用不同进行分类
. └── my_proj ├── crm #代码目录 │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── my_proj #配置文件目录 ├── settings.py ├── urls.py └── wsgi.py
像上面这样,一个文件夹管理多个模块文件,这个文件夹就被称为包
crm/views.py内容
def sayhi(): print('hello world!') print(settings.DATABASES)
. └── my_proj ├── crm #代码目录 │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── my_proj #配置文件目录 ├── settings.py ├── urls.py └── wsgi.py
如何实现在crm/views.py
里导入proj/settings.py
模块?
直接导入的话,会报错,说找到不模块
通过manage.py调用
from crm import views views.sayhi()
输出:
D:Python14_day_training_campPython全栈开发中级第二模块chapter4-常用模块my_proj {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'D:\Python\14_day_training_camp\Python全栈开发中级\第二模块\chapter4-常用模块\my_proj\db.sqlite3'}} hello world!
跨模块导入
目录结构如下
据上面的结构,如何实现在crm/views.py
里导入proj/settings.py
模块?
直接导入的话,会报错,说找到不模块
$ python3 views.py
Traceback (most recent call last):
File "views.py", line 2, in <module>
from proj import settings
ModuleNotFoundError: No module named 'proj'
是因为路径找不到,proj/settings.py 相当于是crm/views.py的父亲(crm)的兄弟(proj)的儿子(settings.py),settings.py算是views.py的表弟啦,在views.py里只能导入同级别兄弟模块代码,或者子级别包里的模块,根本不知道表弟表哥的存在。这可怎么办呢?
答案是添加环境变量,把父亲级的路径添加到sys.path中,就可以了,这样导入 就相当于从父亲级开始找模块了。
crm/views.py中添加环境变量
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print(BASE_DIR) sys.path.append(BASE_DIR) from my_proj import settings def sayhi(): print('hello world!') print(settings.DATABASES)
输出
D:Python14_day_training_campPython全栈开发中级第二模块chapter4-常用模块my_proj {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'D:\Python\14_day_training_camp\Python全栈开发中级\第二模块\chapter4-常用模块\my_proj\db.sqlite3'}}
绝对导入&相对导入
在linux里可以通过cd ..回到上一层目录 ,cd ../.. 往上回2层,这个..就是指相对路径,在python里,导入也可以通过..
例如:
. ├── __init__.py ├── crm │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ ├── views.py #from ..proj import settings ├── manage.py └── proj ├── __init__.py ├── settings.py #from .import urls ├── urls.py └── wsgi.py
views.py里代码
from ..proj import settings def sayhi(): print('hello world!') print(settings.DATABASES)
执行结果报错了
Traceback (most recent call last): File "my_proj/crm/views.py", line 4, in <module> from ..proj import settings SystemError: Parent module '' not loaded, cannot perform relative import
或者有人会看到这个错
ValueError: attempted relative import beyond top-level package
其实这两个错误的原因归根结底是一样的:在涉及到相对导入时,package所对应的文件夹必须正确的被python解释器视作package,而不是普通文件夹。否则由于不被视作package,无法利用package之间的嵌套关系实现python中包的相对导入。
文件夹被python解释器视作package需要满足两个条件:
- 文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件。
- 不能作为顶层模块来执行该文件夹中的py文件(即不能作为主函数的入口)。
所以这个问题的解决办法就是,既然你在views.py里执行了相对导入,那就不要把views.py当作入口程序,可以通过上一级的manage.py调用views.py
. ├── __init__.py ├── crm │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ ├── views.py #from ..proj import settings ├── manage.py #from crm import views └── proj ├── __init__.py ├── settings.py #from .import urls ├── urls.py └── wsgi.py
事实证明还是不行,报错
ValueError: attempted relative import beyond top-level package
但把from ..proj import settings
改成from . import models
后却执行成功了,为什么呢?
from .. import models
会报错的原因是,这句代码会把manage.py所在的这一层视作package,但实际上它不是,因为package不能是顶层入口代码,若想不出错,只能把manage.py往上再移一层。
正确的代码目录结构如下
再执行manage.py就不会报错了。
注:虽然python支持相对导入,但对模块间的路径关系要求比较严格,处理不当就容易出错,so并不建议在项目里经常使用。