2.4.1 Python 包&跨模块导入&绝对导入&相对导入
包(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!')
通过manage.py调用
from crm import views views.sayhi()
执行manage.py (注意这里用python2)
Alexs-MacBook-Pro:my_proj alex$ ls crm manage.py my_proj Alexs-MacBook-Pro:my_proj alex$ python manage.py Traceback (most recent call last): File "manage.py", line 6, in <module> from crm import views ImportError: No module named crm
竟然说找不到模块,为什么呢
包就是目录,但该目录下必须存在 __init__.py 文件, 该文件的内容可以为空。__int__.py用于标识当前目录是一个包。
在crm目录下创建一个空文件__int__.py ,再执行一次就可以了
Alexs-MacBook-Pro:my_proj alex$ touch crm/__init__.py #创建一个空文件 Alexs-MacBook-Pro:my_proj alex$ Alexs-MacBook-Pro:my_proj alex$ ls crm/ __init__.py admin.py models.py views.py __pycache__ apps.py tests.py views.pyc Alexs-MacBook-Pro:my_proj alex$ python manage.py hello world!
注意,在python3里,即使目录下没__int__.py文件也能创建成功,猜应该是解释器优化所致,但创建包还是要记得加上这个文件 吧。
跨模块导入
目录结构如下
. ├── __init__.py ├── crm │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ ├── views.py ├── manage.py └── proj ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py
根据上面的结构,如何实现在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中添加环境变量
import sys ,os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #__file__的是打印当前被执行的模块.py文件相对路径,注意是相对路径 print(BASE_DIR) sys.path.append(BASE_DIR) from proj import settings def sayhi(): print('hello world!') print(settings.DATABASES)
输出
$ python3 views.py /Users/alex/Documents/work/PyProjects/luffy_课件/21天入门/chapter4-常用模块/packages/my_proj ---my proj init--- #proj/__init__.py输出 in proj/settings.py #proj/settings.py输出 {'host': 'localhost'}
*注意;此时在proj/settings.py写上import urls会有问题么?
DATABASES= {
'host':'localhost'
}
import urls #这行刚加的
print('in proj/settings.py')
结果报错了
ModuleNotFoundError: No module named 'urls'
为什么呢? 因为现在的程序入口是views.py , 你在settings.py导入import urls,其实相当于在crm目录找urls.py,而不是proj目录,若想正常导入,要改成如下
DATABASES= { 'host':'localhost' } from proj import urls #proj这一层目录已经添加到sys.path里,可以直接找到 print('in proj/settings.py')
绝对导入&相对导入
在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
from .. import models
会报错的原因是,这句代码会把manage.py所在的这一层视作package,但实际上它不是,因为package不能是顶层入口代码,若想不出错,只能把manage.py往上再移一层
正确的代码目录结构如下
packages/ ├── __init__.py ├── manage.py #from my_proj.crm import views └── my_proj ├── crm │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ ├── views.py #from . import models; from ..proj import settings └── proj ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py
再执行manage.py就不会报错了。
注:虽然python支持相对导入,但对模块间的路径关系要求比较严格,处理不当就容易出错,so并不建议在项目里经常使用。