python中import学习备忘笔记


Posted in Python onJanuary 24, 2017

前言

在python的模块有两种组织方式,一种是单纯的python文件,文件名就是模块名,一种是包,包是一个包含了若干python文件的目录,目录下必须有一个文件__init__.py,这样目录名字就是模块名,包里的python文件也可以通过包名.文件名的方式import

import语法

import语法有两种

1、直接import模块

import Module
 import Module as xx

2、从模块import对象(下级模块,类,函数,变量等)

from Module import Name
 from Module immport Name as yy

as语法是用来设置对象(这里用对象泛指模块,类,函数等等)别名,import将对象名字引入了当前文件的名字空间

假设有如下目录结构

├── A.py
└── pkg
 ├── B.py
 └── __init__.py

在当前目录下,以下语句都是有效的

import A 
import pkg
import pkg.B
from pkg import B

为了简化讨论,下面将不会对as语法进行举例

import步骤

python所有加载的模块信息都存放在sys.modules结构中,当import一个模块时,会按如下步骤来进行

  1. 如果是import A,检查sys.modules中是否已经有A,如果有则不加载,如果没有则为A创建module对象,并加载A
  2. 如果是from A import B,先为A创建module对象,再解析A,从中寻找B并填充到A的__dict__

嵌套import

在import模块时我们可能会担心一个模块会不会被import多次,假设有A,B,C三个模块,A需要import B和C,B又要import C,这样A会执行到两次import C,一次是自己本身import,一次是在import B时执行的import,但根据上面讲到的import步骤,在第二次import时发现模块已经被加载,所以不会重复import

但如下情况却会报错

#filename: A.py
from B import BB
class AA:pass

#filename: B.py
from A import AA
class BB:pass

这时不管是执行A.py还是B.py都会抛出ImportError的异常,假设我们执行的是A.py,究其原因如下

  1. 文件A.py执行from B import BB,会先扫描B.py,同时在A的名字空间中为B创建module对象,试图从B中查找BB
  2. 扫描B.py第一行执行from A import AA,此时又会去扫描A.py
  3. 扫描A.py第一行执行from B import BB,由于步骤1已经为B创建module对象,所以会直接从B的module对象的__dict__中获取BB,此时显然BB是获取不到的,于是抛出异常

解决这种情况有两种办法,

  1. 将from B import BB改为import B,或将from A import AA改为import A
  2. 将A.py或B.py中的两行代码交换位置

总之,import需要注意的是,尽量在需要用到时再import

包的import

当一个目录下有__init__.py文件时,该目录就是一个python的包

import包和import单个文件是一样的,我们可以这样类比:

  • import单个文件时,文件里的类,函数,变量都可以作为import的对象
  • import包时,包里的子包,文件,以及__init__.py里的类,函数,变量都可以作为import的对象

假设有如下目录结构

pkg
├── __init__.py
└── file.py

其中__init__.py内容如下

argument = 0
class A:pass

在和pkg同级目录下执行如下语句都是OK的

>>> import pkg
>>> import pkg.file
>>> from pkg import file
>>> from pkg import A
>>> from pkg import argument

但如下语句是错误的

>>> import pkg.A
>>> import pkg.argument

报错ImportError: No module named xxx,因为当我们执行import A.B,A和B都必须是模块(文件或包)

相对导入和绝对导入

绝对导入的格式为import A.Bfrom A import B,相对导入格式为from . import Bfrom ..A import B,.代表当前模块,..代表上层模块,...代表上上层模块,依次类推。当我们有多个包时,就可能有需求从一个包import另一个包的内容,这就会产生绝对导入,而这也往往是最容易发生错误的时候,还是以具体例子来说明

目录结构如下

app
├── __inti__.py
├── mod1
│ ├── file1.py
│ └── __init__.py
├── mod2
│ ├── file2.py
│ └── __init__.py
└── start.py

其中app/start.py内容为import mod1.file1

app/mod1/file1.py内容为from ..mod2 import file2

为了便于分析,我们在所有py文件(包括__init__.py)第一行加入print __file__, __name__

现在app/mod1/file1.py里用到了相对导入,我们在app/mod1下执行python file1.py或者在app下执行python mod1/file1.py都会报错ValueError: Attempted relative import in non-package

在app下执行python -m mod1.file1python start.py都会报错ValueError: Attempted relative import beyond toplevel package

具体原因后面再说,我们先来看一下导入模块时的一些规则

在没有明确指定包结构的情况下,python是根据__name__来决定一个模块在包中的结构的,如果是__main__则它本身是顶层模块,没有包结构,如果是A.B.C结构,那么顶层模块是A。

基本上遵循这样的原则

  1. 如果是绝对导入,一个模块只能导入自身的子模块或和它的顶层模块同级别的模块及其子模块
  2. 如果是相对导入,一个模块必须有包结构且只能导入它的顶层模块内部的模块

有目录结构如下

A
├── B1
│ ├── C1
│ │ └── file.py
│ └── C2
└── B2

其中A,B1,B2,C1,C2都为包,这里为了展示简单没有列出__init__.py文件,当file.py的包结构为A.B1.C1.file(注意,是根据__name__来的,而不是磁盘的目录结构,在不同目录下执行file.py时对应的包目录结构都是不一样的)时,在file.py中可采用如下的绝对的导入

import A.B1.C2
import A.B2

和如下的相对导入

from .. import C2
from ... import B2

什么情况下会让file.py的包结构为A.B1.C1.file呢,有如下两种

  1. 在A的上层目录执行python -m A.B1.C1.file, 此时明确指定了包结构
  2. 在A的上层目录建立文件start.py,在start.py里有import A.B1.C1.file,然后执行python start.py,此时包结构是根据file.py__name__变量来的

再看前面出错的两种情况,第一种执行python file1.pypython mod1/file1.py,此时file.py__name____main__ ,也就是说它本身就是顶层模块,并没有包结构,所以会报错

第二种情况,在执行python -m mod1.file1python start.py时,前者明确告诉解释器mod1是顶层模块,后者需要导入file1,而file1.py__name__mod1.file1,顶层模块为也mod1,所以在file1.py中执行from ..mod2 import file2时会报错 ,因为mod2并不在顶层模块mod1内部。通过错误堆栈可以看出,并不是在start.py中绝对导入时报错,而是在file1.py中相对导入报的错

那么如何才能偶正确执行呢,有两种方法,一种是在app上层目录执行python -m app.mod1.file1,另一种是改变目录结构,将所有包放在一个大包中,如下

app
├── pkg
│ ├── __init__.py
│ ├── mod1
│ │ ├── __init__.py
│ │ └── file1.py
│ └── mod2
│ ├── __init__.py
│ └── file2.py
└── start.py

start.py内容改成import pkg.mod1.file1,然后在app下执行python start.py

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者使用python能带来一定的帮助,如有疑问大家可以留言交流。

Python 相关文章推荐
在Python中操作列表之list.extend()方法的使用
May 20 Python
Python实现购物车购物小程序
Apr 18 Python
Django如何自定义分页
Sep 25 Python
python使用flask与js进行前后台交互的例子
Jul 19 Python
python通过http下载文件的方法详解
Jul 26 Python
python进程的状态、创建及使用方法详解
Dec 06 Python
基于TensorFlow中自定义梯度的2种方式
Feb 04 Python
python图形开发GUI库pyqt5的详细使用方法及各控件的属性与方法
Feb 14 Python
python pandas.DataFrame.loc函数使用详解
Mar 26 Python
python实现数据结构中双向循环链表操作的示例
Oct 09 Python
如何用python反转图片,视频
Apr 24 Python
Python 用户输入和while循环的操作
May 23 Python
用python实现简单EXCEL数据统计的实例
Jan 24 #Python
Python如何import文件夹下的文件(实现方法)
Jan 24 #Python
利用Python脚本实现ping百度和google的方法
Jan 24 #Python
解决python2.7用pip安装包时出现错误的问题
Jan 23 #Python
浅谈终端直接执行py文件,不需要python命令
Jan 23 #Python
在Linux命令行终端中使用python的简单方法(推荐)
Jan 23 #Python
Python 详解基本语法_函数_返回值
Jan 22 #Python
You might like
php实现两表合并成新表并且有序排列的方法
2014/12/05 PHP
PHP开发中解决并发问题的几种实现方法分析
2017/11/13 PHP
php实现mysql连接池效果实现代码
2018/01/25 PHP
为EasyUI的Tab标签添加右键菜单的方法
2012/07/14 Javascript
JavaScript中统计Textarea字数并提示还能输入的字符
2014/06/10 Javascript
js实现图片和链接文字同步切换特效的方法
2015/02/20 Javascript
详解Wondows下Node.js使用MongoDB的环境配置
2016/03/01 Javascript
javascript实现dom元素可拖动
2016/03/21 Javascript
创建基于Bootstrap的下拉菜单的DropDownList的JQuery插件
2016/06/02 Javascript
spring+angular实现导出excel的实现代码
2019/02/27 Javascript
JavaScript中的各种宽高属性的实现
2020/05/08 Javascript
python 获取文件下所有文件或目录os.walk()的实例
2018/04/23 Python
django 删除数据库表后重新同步的方法
2018/05/27 Python
Python3自动签到 定时任务 判断节假日的实例
2018/11/13 Python
Python3非对称加密算法RSA实例详解
2018/12/06 Python
python之验证码生成(gvcode与captcha)
2019/01/02 Python
Python实现的登录验证系统完整案例【基于搭建的MVC框架】
2019/04/12 Python
django 消息框架 message使用详解
2019/07/22 Python
Pycharm添加虚拟解释器报错问题解决方案
2020/10/13 Python
jupyter notebook远程访问不了的问题解决方法
2021/01/11 Python
涂鸦板简单实现 Html5编写属于自己的画画板
2016/07/05 HTML / CSS
西班牙英格列斯百货官网:El Corte Inglés
2016/09/25 全球购物
UGG英国官方网站:UGG UK
2018/02/08 全球购物
应聘自荐书
2013/10/08 职场文书
十八大感想感言
2014/02/10 职场文书
物控部经理职务说明书
2014/02/25 职场文书
新学期决心书
2014/03/11 职场文书
集体生日活动方案
2014/08/18 职场文书
国家助学金感谢信
2015/01/21 职场文书
2016年度基层党建工作公开承诺书
2016/03/25 职场文书
Ajax常用封装库——Axios的使用
2021/05/08 Javascript
MySQL中CURRENT_TIMESTAMP的使用方式
2021/11/27 MySQL
HTML基本元素标签介绍
2022/02/28 HTML / CSS
Python中非常使用的6种基本变量的操作与技巧
2022/03/22 Python
vue项目如何打包之项目打包优化(让打包的js文件变小)
2022/04/30 Vue.js
MySQL数据库中的锁、解锁以及删除事务
2022/05/06 MySQL