包(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

其实这两个错误的原因归根结底是一样的:在涉及到相对导入时,package所对应的文件夹必须正确的被python解释器视作package,而不是普通文件夹。否则由于不被视作package,无法利用package之间的嵌套关系实现python中包的相对导入。

文件夹被python解释器视作package需要满足两个条件:

  1. 文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件。
  2. 不能作为顶层模块来执行该文件夹中的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往上再移一层。

正确的代码目录结构如下

    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—包介绍的更多相关文章

  1. python包管理-distutils,setuptools,pip,virtualenv等介绍

    python包管理-distutils,setuptools,pip,virtualenv等介绍 对于每个编程语言来说打包和发布开发包往往非常重要,而作为一个编程者能够快速容易的获得并应用这些由第三方 ...

  2. Python包管理工具介绍

    常见的包管理工具及关系 setuptools -->distribute easy_install-->pip 1.distribute distribute是对标准库disutils模块 ...

  3. 【转】linux和windows下安装python集成开发环境及其python包

    本系列分为两篇: 1.[转]windows和linux中搭建python集成开发环境IDE 2.[转]linux和windows下安装python集成开发环境及其python包 3.windows和l ...

  4. 从零开始学Python第0周:Python基本介绍(部分内容来源于网络)

    Python入门介绍 一,Python的基本介绍 (1)概要 Python是一种解释型,面向对象,动态数据类型的高级程序设计语言.常被广泛用于处理系统管理任务和web编程.现如今Python已经成为了 ...

  5. Python 包管理工具解惑

    Python 包管理工具解惑 本文链接:http://zengrong.net/post/2169.htm python packaging 一.困惑 作为一个 Python 初学者,我在包管理上感到 ...

  6. 【转】Python Twisted介绍

    Python Twisted介绍 作者:Jessica McKellar 原文链接 Twisted是用Python实现的基于事件驱动的网络引擎框架.Twisted诞生于2000年初,在当时的网络游戏开 ...

  7. Python包管理工具和多版本环境管理

    1. Python包管理工具 在安装Python包的过程中,经常涉及到distutils.setuptools.distribute.setup.py.easy_install.easy_instal ...

  8. [Python]Conda 介绍及常用命令

    一.介绍 Anaconda 是一个用于科学计算的 Python 发行版,支持 Linux, Mac, Windows, 包含了众多流行的科学计算.数据分析的 Python 包.其使用conda系统进行 ...

  9. Python包和版本管理的最好工具----pipenv

    pipenv 是Kenneth Reitz大神的作品,提供Python的各个版本间的管理,各种包管理.个人觉得是virtualenv pip等工具的合体. pipenv主要有以下特性: (1)以前我们 ...

随机推荐

  1. [20181219]script使用小技巧.txt

    [20181219]script使用小技巧.txt --//前几天在使用strace时遇到问题,它的输出使用标准错误句柄.--//我在想平时使用sqlplus如果输出字段很多,屏幕看起来一片混乱.-- ...

  2. linux下objdump应用

    <a href="http://www.maomao365.com/?p=952" > linux命令objdump的用法  http://www.maomao365. ...

  3. c/c++ 类模板初探

    类模板 1,模板类里的函数都是模板函数 2,模板类里的函数,在类外面实现的时候,要用模板函数(方法:push_back)的方式实现,在类内部实现时,不需要用模板函数(方法:show)方式实现. 3,用 ...

  4. LeetCode算法题-Add Binary(Java实现)

    这是悦乐书的第157次更新,第159篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第16题(顺位题号是67).给定两个二进制字符串,返回它们的总和(也是二进制字符串).输 ...

  5. spring简述

    1. 什么是Spring Spring:SE/EE开发的一站式框架.(有EE开发每一层的解决方案) WEB层:SpringMVC Service层:Spring的Bean管理,Spring的声明式事务 ...

  6. nginx学习笔记(三)

    浏览器缓存

  7. 在Ubuntu上安装Jenkins

    先决条件 安装Java SDK sudo apt-get install openjdk-8-jdk # sudo apt-get install openjdk-7-jdk 早些系统可以安装 第1步 ...

  8. Linux:Day5 shell编程初步、grep

    bash的基本特性(3) 1.提供了编程环境 程序编程风格: 过程式:以指令为中心,数据服务于指令: 对象式:以数据为中心,指令服务于数据: shell程序:提供了编程能力,解释执行:过程式.解释执行 ...

  9. 三、Oracle 查询+where条件

    一.查询1.distinct:查询去除重复的行,是所有的列都重复才满足条件2.列别名:as或者空格 select name as 姓名 from student3.查询字段可以做数学运算,也可以做字符 ...

  10. 20175330 2018-2019-2 《Java程序设计》第八周学习总结

    # **教材学习内容总结### 本周学习<Java程序设计>第十五章:*** 泛型: 泛型(Generics)的主要目的是可以建立具有类型安全的集合框架,如链表.散列映射等数据结构.泛型类 ...