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 相关文章推荐
Django框架下在视图中使用模版的方法
Jul 16 Python
WINDOWS 同时安装 python2 python3 后 pip 错误的解决方法
Mar 16 Python
浅谈pandas中Dataframe的查询方法([], loc, iloc, at, iat, ix)
Apr 10 Python
python+influxdb+shell编写区域网络状况表
Jul 27 Python
pandas去重复行并分类汇总的实现方法
Jan 29 Python
python爬虫的一个常见简单js反爬详解
Jul 09 Python
在自动化中用python实现键盘操作的方法详解
Jul 19 Python
pytorch 共享参数的示例
Aug 17 Python
selenium 多窗口切换的实现(windows)
Jan 18 Python
python opencv 实现读取、显示、写入图像的方法
Jun 08 Python
Python接口自动化系列之unittest结合ddt的使用教程详解
Feb 23 Python
Python编程super应用场景及示例解析
Oct 05 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模板页面中分页代码的解析
2009/02/06 PHP
php验证手机号码(支持归属地查询及编码为UTF8)
2013/02/01 PHP
利用php操作memcache缓存的基础方法示例
2017/08/02 PHP
jquery 分页控件实现代码
2009/11/30 Javascript
js的写法基础分析
2011/01/17 Javascript
说说JSON和JSONP 也许你会豁然开朗
2012/09/02 Javascript
jQuery读取和设定KindEditor值的方法
2013/11/22 Javascript
JS短路原理的应用示例 精简代码的途径
2013/12/13 Javascript
NodeJs中的VM模块详解
2015/05/06 NodeJs
简介JavaScript中toTimeString()方法的使用
2015/06/12 Javascript
全面解析Bootstrap表单使用方法(表单控件)
2015/11/24 Javascript
基于javascript数组实现图片轮播
2016/05/02 Javascript
react-router实现跳转传值的方法示例
2017/05/27 Javascript
JavaScript中运算符规则和隐式类型转换示例详解
2017/09/06 Javascript
详解webpack 打包文件体积过大解决方案(code splitting)
2018/04/10 Javascript
webstorm中vue语法的支持详解
2018/05/09 Javascript
最后说说Vue2 SSR 的 Cookies 问题
2018/05/25 Javascript
6行代码实现微信小程序页面返回顶部效果
2018/12/28 Javascript
深入了解js原型模式
2019/05/30 Javascript
Vue-cli3.X使用px2 rem遇到的问题及解决方法
2019/08/08 Javascript
JS实现canvas简单小画板功能
2020/06/23 Javascript
jquery插件懒加载的示例
2020/10/24 jQuery
使用go和python递归删除.ds store文件的方法
2014/01/22 Python
python中使用enumerate函数遍历元素实例
2014/06/16 Python
详解Python3操作Mongodb简明易懂教程
2017/05/25 Python
python实现归并排序算法
2018/11/22 Python
利用python提取wav文件的mfcc方法
2019/01/09 Python
python 图像处理画一个正弦函数代码实例
2019/09/10 Python
python实现百度OCR图片识别过程解析
2020/01/17 Python
办公室前台的岗位职责
2013/12/20 职场文书
会展策划与管理专业求职信
2014/06/09 职场文书
环境科学专业教师求职信
2014/07/12 职场文书
超市仓管员岗位职责范本
2014/09/18 职场文书
2015年环境整治工作总结
2015/05/22 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
springboot如何初始化执行sql语句
2021/06/22 Java/Android