关于import语句

  python程序需要使用某个第三方模块的话要用import语句,其实就是把目标模块的内容加载到内存里。当然,在加载之前,python会按照一定的顺序寻找sys.path中的目录。sys.path中的第一项非常不起眼是'',但是千万要注意这是指当前工作目录。也就是说,如果当前工作目录下恰好有和你想import的模块名同名脚本的话,python是优先把那个脚本import进来,然后就停止搜索了。python中所有加载到内存中的模块都会记录到sys.modules中,这是一个模块名和模块文件的对应字典。当语句中出现import语句的时候,python首先会到这个字典中去找,如果找不到,就搜索sys.path给出的一些路径,寻找相关模块。找到之后就把它加入内存并载入sys.modules。需要注意的是import A.B的时候,解释器会先寻找A模块,然后再寻找A.B,A模块和A.B模块都会被记录到sys.module中去。

  在确认内存中已经加载了相关模块之后,python就会把这个模块名保存到local的命名空间中,如果有import...as..的话就以as为准。不过即使有as的时候,加载到sys.modules里面去的仍然是真正的模块名。

  对于 from A import B这样的形式的话,python首先在sys.modules中找模块A.B,找到的话就获得了这个模块的对象而直接加载,没找到的话就新建一个空的对象<module xx>。再获取这个对象的__dict__(当然,一个空对象的__dict__肯定也是空的,所以python会先解析模块名.py来充实__dict__。),从中寻找相关子方法或子对象的信息,找不到则报错。

  *__dict__是一个类或者实例的固有属性,其内容是其所有成员属性和成员方法的名字以及内容的对应。

  比如下面这样一个示例:

#在A.py中:
from B import D
class C:
pass #在B.py中:
from A import C
class D:
pass

  这样两个模块的话,先python A.py 来执行前者,首先python建立B.py的空对象,尝试解析B.py对象的__dict__,当解析到B的第一行时,又建立了A.py的空对象,然后为了得到A.py的__dict__又跑回去解析A.py。那么又碰到了A的第一句,但此时B.py的对象已经在内存中存在,但是它的__dict__仍然是空的没有被充实,所以最终会报ImportError:无法找到D

  如果是import一整个包而不是一个文件的话,那就是解析这个包里的__init__.py文件。

■  注意文件本身不能和module名重名

  今天写了一个email.py脚本里import了email模块,然后如果就在存放email.py的这个脚本的目录下运行它就报错了。这是因为python的工作目录是email.py存放的目录时,python优先搜索当前工作目录下的模块名(见上面的说明)

■  python包结构简单介绍

  在构建python包的时候,我们经常会看到__init__.py这个文件。含有__init__.py这个文件的目录可以认为是一个python的package,可以被其他文件导入的。一个简单的包就是下面这样子的:

#######目录结构
# ./
# | test.py 这个是我们用来测试导入包的脚本,是__main__模块
# | main/ 测试中用到的包
# | __init__.py 这里面现在是空的,这个文件只为了表明main是个package
# | funcs.py 这里面定义一个函数func()供外部调用
######## ####test.py中####
import main
import main.funcs
from main.funcs import func
#import main.funcs.func

  首先可以看出来,因为__init__.py的存在,main是一个包,所以在外界使用是可以直接import它的包名。以上前三个语句都是可以正确执行的,最后一个被注释的语句会报错,这说明在import语句中用圆点符来表示层级的时候最低只能表示到模块层(或者脚本这一层),而不能深入到变量或者函数层。如果想指定某个变量或者函数直接导入,那么就要用from xx import xx这样的形式了。在第两句那样的情况中,为了使用func,我们可以main.funcs.func来调用,所以说在普通语句中的原点符可以深入到变量或者函数层级中。但是在只有第一句的情况下,我们可能没有办法调用到func,因为此时写main.funcs.func是会报错的,中间那一节funcs并不会随着import main这个语句加载进来,具体可以在Import main之后dir(main)或者看下main.__dict__来看,是看不到funcs这个的。那么是不是说import main一无是处了呢?也不见得:

  我们再回头仔细看__init__.py这个文件,刚才假定了这个文件是空的,其实这里面也可以写很多东西,比如我在__init__.py中增加了一个名为var_p的变量。那么在test.py中要如何使用这个变量?很简单,就是import main然后再调用main.var_p,或者直接from main import var_p这个var_p除了是一个变量,也可以是一个函数,一个类等等。这就是说,在__init__.py这个文件中写的东西,被视为“包直属”的一些内容,所以通过包名main来直接调用。反过来说,__init__.py其实是在别人调用包名时指向的那个文件。比如此时我们再查看dir(main)就可以看到var_p的存在了。

  另外在这样的情景下还有一个小问题,加入__init__.py中定义了一个函数叫funcs,然后main包中又有一个模块叫funcs,引用这两个funcs好像都是用main.funcs。在import的时候我们可以区分开他们,因为作为函数的那个funcs是不能直接被import main.funcs的,而在from main import funcs的时候因为搜索命名空间的就近原则,导入的是函数funcs(前提是在此之前不能导入过main.funcs,否则会因为一些原因导致最终还是导入module的funcs,至于这个原因是什么,请看上面关于from import 和import 机制上的一个区别)。在使用的时候,一般情况下模块会是main.funcs来调用而函数会是funcs来调用,那如果在import模块的时候用了import main.funcs as funcs呢?这个的话就牵扯到命名空间的覆盖,如果先from main import funcs后然后再import main.funcs as funcs的话,模块funcs会覆盖函数funcs在命名空间中的地位,所以最终的funcs是模块funcs。再深入问一个,假如刚才两句语句反过来呢??从结果上来说没什么好说的,因为反过来即使没有as关键字的时候,funcs也是模块funcs。有了as关键字之后,尽管命名空间中会多出一个funcs来,但是加载到sys.modules里面去的仍然是main.funcs,所以from import 的时候依然会先找到模块的main.funcs,所以from import的结果依然是模块funcs。

  python的package之间还可以进行嵌套,比如可以有一个叫app的package,这意味着存在app/__init__.py这个文件,然后app下面可以有个子目录叫main,main里有个__init__.py,这样main就算是一个子package,但也是一个package。嵌套包含关系的包自然就需要用.来导入,比如上面说的就可以import app.main这样的感觉来导入。类似的,import 进来的app.main其实是代表了main/__init__.py这个文件,app.main.var可以访问这个init文件中定义的叫var的变量or函数or类等。

■  关于包内导入和relative import

  上面讲了一大堆比较复杂,究其复杂的原因,就是因为我们没有办法通过main.funcs.func来直接调用包中某个模块中的某个对象。其实聪明的同学可能能想到,之前我们在__init__.py里手动定义了一个funcs可以直接拿来用,那么可不可以把模块funcs也弄到__init__.py里面去。这样不就能直接用了。很自然,就会想到在__init__.py中写上 import funcs即可。这个就是包内导入了(__init__.py和funcs.py在同一个包内)。这是一个最为简单的包内导入的例子,如果想要用from import 的形式来import funcs这个模块的话,可以这么写:from . import funcs。这个“.”在这里,代表的就是当前目录的意思,而这种导入方法就是所谓的relative import 即相对导入了。

  和所有“相对”和“绝对”的东西一样,这个相对导入更加灵活。在from后面加一个点就是指当前目录,加上两个点就是从上级目录导入。如果在点后面直接接上某个名字那就是说从点的数量指向的那个目录下找到相应名字的模块,from这个模块去import一些东西。

  有时候,我们在自己自定义的包里里面可能碰巧有和内建模块重名的文件出现,此时在导入的时候解释器可能会闹不清该导入什么模块,此时可以用相对导入来明确指出我需要的是我自己定义的模块而不是内建的。(然而绝对导入时似乎也是这样的。。不知道为甚网上很多人都这么写,闹得不太清楚。。)

  需要注意的是,使用了相对导入的文件只能作为module来用,而不能作为__main__模块来直接运行,否则会报Attempted relative import in non-package错。这是因为相对导入时找路径是通过模块名__name__去找的,如果模块名不是脚本名而是__main__了那么自然是找不到了。

【Python】 关于import和package结构的更多相关文章

  1. Python Import机制备忘-模块搜索路径(sys.path)、嵌套Import、package Import

    出处:http://blog.csdn.net/kernelspirit/article/details/3381666 最近在看<Python源码剖析>,对Python内部运行机制比以前 ...

  2. [转] Python的import初探

    转载自:http://www.lingcc.com/2011/12/15/11902/#sec-1 日常使用python编程时,为了用某个代码模块,通常需要在代码中先import相应的module.那 ...

  3. 解析Python编程中的包结构

    解析Python编程中的包结构 假设你想设计一个模块集(也就是一个"包")来统一处理声音文件和声音数据.通常由它们的扩展有不同的声音格式,例如:WAV,AIFF,AU),所以你可能 ...

  4. python 的import机制2

    http://blog.csdn.net/sirodeng/article/details/17095591   python 的import机制,以备忘: python中,每个py文件被称之为模块, ...

  5. python之import机制

    1. 标准 import        Python 中所有加载到内存的模块都放在 sys.modules .当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将 ...

  6. 关于Python的import机制原理

    很多人用过python,不假思索地在脚本前面加上import module_name,但是关于import的原理和机制,恐怕没有多少人真正的理解.本文整理了Python的import机制,一方面自己总 ...

  7. package结构

    1.package结构 一个package下常见的文件. 路径有:├── CMakeLists.txt #package的编译规则(必须)├── package.xml #package的描述信息(必 ...

  8. Python如何import文件夹下的文件

    Python的import包含文件功能就跟PHP的include类似,但更确切的说应该更像是PHP中的require,因为Python里的import只要目标不存在就报错程序无法往下执行.要包含目录里 ...

  9. python 的 import 使用规则

    对于含有 __init__.py 的目录(如adir),其实它就是一个package,它的子目录如果也包含 __init__.py,则只要将 adir 加入 sys.path,则它的字目录就不用加了, ...

随机推荐

  1. javascript parseint

  2. 图像处理------K-Means算法演示

    一:数学原理 K-Means算法的作者是MacQueen, 基本的数学原理很容易理解,假设有一个像素 数据集P.我们要根据值不同将它分为两个基本的数据集合Cluster1, Cluster2,使 用K ...

  3. org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException

    1.错误原因 org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't ...

  4. 1118: 属于 static 类型 Object 的值的隐式强制指令的目标可能是非相关类型 Number。

    1.错误描述 此行的多个标记: -1118: 属于 static 类型 Object 的值的隐式强制指令的目标可能是非相关类型 Number. -left 2.错误原因 /** * 刷新按钮函数 */ ...

  5. web开发性能优化---SEO优化篇

    一.清理垃圾代码 清理垃圾代码是指删除页面中的冗余代码,可以删除80%的冗余代码. 垃圾代码主要指那些删除了也不会对页面有任何影响的非必要代码. 最常见的垃圾代码,空格 空格字符是网页中最常见的垃圾代 ...

  6. hibernate学习(一)配置,导包

    框架的作用 学过javaWeb基础的已经对web层 jsp  servlet   ,service  层  ,dao层的jdbc .DBUtils 有了很深的了解 并编写代码实现某种功能 为了提高开发 ...

  7. Struts2(七) Struts2访问Servlet的API

    当接受表单参数,向页面保持数据时.要用到Struts访问Servlet 的API .下面只做参考,有错误或不同意见可以发送邮箱2440867831@qq.com  .建议大家看struts文档,源代码 ...

  8. ajaxfileupload原理及用法,主要用于即想用ajax序列化传递参数,又必须上传文件

    一,原理 AjaxFileUpload.js并不是一个很出名的插件,只是别人写好的放出来供大家用,原理都是创建隐藏的表单和iframe然后用JS去提交,获得返回值. 当初做了个异步上传的功能,选择它因 ...

  9. JNDI在server.xml中的配置(全局和局部的)

    总结: 全局就是在数据源server.xml中配置,然后通过和项目名相同的xml来进行映射.对所有的项目都起作用.那个项目需要就在对应的tomcat下配置一个与项目名相同的xml映射文件. 局部的就是 ...

  10. 【NFS】nfs安装调优

    nfs [root@flymaster ~]# rpm -qa nfs-utils rpcbindnfs-utils-1.2.3-75.el6.x86_64rpcbind-0.2.0-13.el6_9 ...