在Python中,一个.py文件代表一个Module。在Module中可以是任何的符合Python文件格式的Python脚本。了解Module导入机制大有用处。

1 Module组成

一个.py文件就是一个module。Module中包括attribute, function等。 这里说的attribute其实是module的global variable。

在一个ModuleTests.py文件中:

#!python
#-*- coding: utf-8 -*- """
全局变量
""" # hello doc
global moduleName
moduleName = __name__
a = 1 def printModuleName():
print(a+1)
print(__name__)
print(moduleName) '''
if __name__ == '__main__' :
print('current module name is "' + __name__+'"')
''' printModuleName()
print(a)
print(dir()) import __builtin__
print(__builtin__ == __builtins__)
print(__doc__)
print(__file__)
print(__name__)
print(__package__)
__name__ = 'hello'
print(__name__)

除了你自己定义的那些全局变量和函数外,每一个module还有一些内置的全局变量。在这个module就包括了三个attribute:a,moduleName,printModuleName。如果该模块被导入到另一个模块,在另个一模块中,就可以通过某种方式来访问这三个attribute。

1.1 Module 内置全局变量

每一个模块,都会有一些默认的attribute(全局变量)。dir()函数 是python中的一个顶级函数,勇于查看模块内容。例如上面的例子中,使用dir()查看结果是:

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'moduleName', 'printModuleName']。其中a, moduleName, printModuleName 是由用户自定义的。其他的全是内置的。

1)__name__ :模块的名称。例如上面的ModuleTests.py,模块的名称默认就是ModuleTests。在运行时,如果一个module是程序入口,那么__name__就是”__main__”。它是最常用的。

2)__builtins__:在Python中有一个内置的module,叫做:__builtin__,它是一个Python的模块。而任何一个Python的模块都有一个__builtins__全局变量,它就是内置模块__builtin__的引用。可以通过如下代码测试:

import __builtin__
print(__builtin__ == __builtins__)

// 测试结果是True

在Python代码里,不需要我们导入就能直接使用的函数,类等,都是在这个内置模块里的。例如:range(),bytes(),dir()。

3)__doc__:module的文档说明。即便是Python的初学者都知道Python中的多行注释是用三对单引号或者双引号包含的。网上有人说__doc__其实就是注释,这句话呢说的太随意容易给人误解。经过测试,模块的__doc__应该是:文件头之后代码(包含import)之前 第一个 多行注释。 方法的__doc__是方法前的那个注释。

在交换模式下,我们可以直接使用__doc__来查看方法的说明的。例如查看string.split方法的说明:str.split.__doc__就可以了。

4)__file__:当前module所在的文件的路径。

5)__package__:当前module所在的包名。如果没有,为None。

1.2 dir()的妙用

dir()是一个内置函数,用于查找指定的module中包括哪些attribute和method (或者function)。如果不指定参数,默认是当前module。上面说了range,dir,bytes等都是在内置模块里的,那么到底是不是呢?

dir(__builtins__)就可看到了:

2 Module导入

2.1 导入及其使用

一个Module可以导入(import)到其他的Python脚本中使用。导入方式有多种:

1)import module1

2)import module1 as m1

3)from module1 import xxx

4)from module1 import xxx as yyy

从包(package)导入,也分为类似的三种:

1)import p1.p2.p3.module1

2)import p1.p2.p3.module1 as m1

3)from p1.p2.p3.module1 import xxx

4)from p1.p2.p3.module1 import xxx as yyy

假设module1有两个attribue: a1,a2, 两个function: f1,f2下面来说明这几种导入方式的区别:

方式一是导入整个module1, 并将赋值给一个变量module1,来供使用。使用时,可以使用module1.a1, module1.a2, module1.f1(params), module.f2(params)

方式二是在方式一的基础上,重命名为m1,也就是说使用时得使用: m1.a1, m1.a2, m1.f1, m1.f2。

方式三是导入模块的部分内容(导入一个或者一些attribute或者function) 。例如 from module1 import a1,导入完成后,在当前的模块中创建了一个 a1的变量。调用是直接调用a1即可。

方式四对于导入一个attribute或者function时,可以重命名。例如 from module1 import a1 as msg,那么导入完毕,就是在当前的模块中创建了一个msg的变量,指向了module1.a1。 我们在调用时,只能通过msg来调用。

2.2 一次加载多次导入

对于上面的4种导入方式,不论哪一种,都有两个阶段:1)找到module对象,2)按需分配给变量。

模块本身就是为了复用的。在一个大的项目中,一些基础的、公共的模块通常会被大量使用,也就是说会被很多的module导入使用。我们也知道,module是放在py文件中的。如个一个module被大量导入时,难道要每一次导入,都去磁盘上找一py文件吗?

显然不能这样设计,如果真的这样设计,程序的性能将是极差的了。

对于同样的问题,Java中的做法是,使用ClassLoader加载类,并采用父加载器委托机制。尽可能的保证,同一个ClassLoader下,在多次引用一个类时,都是同一个。我们可以将该方式称为一次加载,多地使用。

Python的设计者,也考虑到这个问题。也采用了类似方案,被我称为一次加载,多次导入。我们假设它有一个Module Loader的存在,在首次加载(其实是首次import)时,执行流程如下:

1)由Module Loader从检索路径下找出相应的模块

2)编译或者找到合适的字节码文件(.pyc结尾)

3)解释执行要导入的Module,并放入缓存。

4)将导入的Module对象(或者其属性)分配给当前Module下的变量。

随后整个程序中再有执行import该moudle时,只需要从缓存中拿到该module,然后执行4)。

此外,对于过程2)有这样4种情况:

A: 若.py与.pyc都存在:会对.py文件的最后修改时间与.pyc文件的最后修改时间比较。执行时间靠后的那个。

B: 若.py与.pyc都不存在,继续找,如果最终都没有找到,出错。

C: 若.py存在,.pyc不存在:编译.py为.pyc。

D:若.py不存在,.pyc存在,直接执行.pyc。

再者还要说明2点:

1)一次加载,多次导入的机制,在Python程序包中提供的交互式命令行里使用import是不管用的。在交互式下,一次加载只能用于一次导入。

2)一般main py是不会被编译成pyc的,一个模块要想被编译成pyc,需要import到其他模块才行。

2.3 搜索路径

依据Java编程经验来看,通常程序会将文件放在不同的地方。Python必然也不例外。Python的搜索顺序为:

1)  已加载模块的缓存

2)  内置模块

3)  sys.path

其中sys.path包含以下几部分:

1)入口程序的目录

2)系统环境变量PYTHONPATH代表的目录

3)标准Python库目录

4)任何.pth文件的内容(如果存在的话)

下面使用命令看一下sys.path的目录有哪些:

['',
'C:\\windows\\SYSTEM32\\python27.zip',
'D:\\Program Files\\Python\\Python27\\DLLs',
'D:\\Program Files\\Python\\Python27\\lib',
'D:\\Program Files\\Python\\Python27\\lib\\plat-win',
'D:\\Program Files\\Python\\Python27\\lib\\lib-tk',
'D:\\Program Files\\Python\\Python27',
'D:\\Program Files\\Python\\Python27\\lib\\site-packages']

如果要加载的module不在上述目录下,可以通过3钟手段:

1)  配置环境变量PYTHONPATH,配置是与环境变量PATH的风格一样。

2)  程序动态修改sys.path

3)  放到site-packages目录下。

2.4 reload()

有些情况下,我们需要在程序运行是对程序代码做修改。例如我们需要监控某一方法执行快慢,是否存在性能问题时。我们需要在function的开始、结束部分记录一个startTime,endTime,依此来判定执行性能时。像这样的场景下,因为Module的加载一次,多长调用的机制,我们修改完代码,也不会生效。此时就需要一种机制来重新加载module,以达到期望效果。reload() 就可以解决这个问题。

reload(module) 是一个函数,参数是一个module对象。执行reload,就会等于再一次进行加载。

3 Package

3.1 __init__.py

每一个package下必须有一个__init__.py文件,该文件用于表明当前目录可以作为一个package。

__init__.py 也是一个python,当首次加载相应的package时,会执行__init__.py。

__init__.py文件可以什么也没有,也可以指定__all__或(和)__path。

3.2 __all__

__all__的值是一个列表,用于当程序中使用 from pkg1.pkg2.pkg3 import * 时。

就拿Python_HOME/Lib/下的json包来做实验,由于该文件比较大,我就写出主要部分:

__version__ = '2.0.9'
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder',
]
__author__ = 'Bob Ippolito <bob@redivi.com>' from .decoder import JSONDecoder
from .encoder import JSONEncoder def dump(params):
pass def dumps(params):
pass def load(params):
pass def loads(params):
pass

目录结构如下:

当程序中使用 from json import * 引起json包首次加载时,执行过程如下:

1)找到json目录,执行__init__.py 执行完毕后:包下会暴漏出:load,loads,dump.dumps, JSONDecoder, JSONEncoder

2)查找* ,即从__all__找出要导出的变量。

当程序使用Import json.encoder引起json包首次加载时,执行过程如下:

1)找到json目录,执行__init__.py 执行完毕后:包下会暴漏出:load,loads,dump.dumps, JSONDecoder, JSONEncoder (以供from json import * 使用)

2)导入json包到一个变量里(此后程序可以直接使用json.encoder, json.decoder,json.scanner)

上述结论,来源于下面的测试用例:

#!python
#-*- coding: utf-8 -*- """
Package Import Test
""" #from json import *
from json import encoder
import json print(json.encoder == encoder)
print(json.decoder is None)
print(json.scanner is None)
print(dir())

3.3 __path__

该变量用于配置包下的搜索位置。例如:

在Utils下增加2个目录Linux和Windows, 并各有一个echo.py文件, 目录如下

 Sound/Utils/
|-- Linux 目录下没有__init__.py文件, 不是包, 只是一个普通目录
| `-- echo.py
|-- Windows 目录下没有__init__.py文件, 不是包, 只是一个普通目录
| `-- echo.py
|-- __init__.py
|-- echo.py
|-- reverse.py
`-- surround.py

如果__init__.py是空的,当使用import Sound.Utils.echo导入echo时,会导入的是Sound/Utils/echo.py。

接下来我将__init__.py做如下修改:

import sys
import os print "Sound.Utils.__init__.__path__ before change:", __path__ dirname = __path__[0]
if sys.platform[0:5] == 'linux':
__path__.insert( 0, os.path.join(dirname, 'Linux') )
else:
__path__.insert( 0, os.path.join(dirname, 'Windows') )
print "Sound.Utils.__init__.__path__ AFTER change:", __path__

在Linux上执行import Sound.Utils.echo,那么搜索路径就会变成了: 'Sound/Utils/Linux', 'Sound/Utils'。以此来达到自动化的按需加载响应的module的功能。

Python : Module的更多相关文章

  1. install python module

    [install python module] 参考:http://docs.python.org/2.7/install/index.html

  2. Nuke Python module的使用

    最近很多脚本工作都需要脱离nuke的gui环境运行,没有了script editor就必须要尝试Nuke Python module功能了.该模式可以执行大部分在GUI环境中的命令,在自动生成或者批量 ...

  3. __import__ 与动态加载 python module

    原文出处: koala bear    Direct use of __import__() is rare, except in cases where you want to import a m ...

  4. Python module中的全局变量

    Python module中的全局变量 我想要实现一个python module,这个module中有一些配置项,这些配置项可以被读取,被修改.一个可行的方案是把这些配置项写到一个叫settings. ...

  5. Python module all in one

    Python module all in one Python Modules https://docs.python.org/3/tutorial/modules.html Fibonacc # F ...

  6. Python.Module.site

    site " This module is automatically imported during initialization. The automatic import can be ...

  7. import 本地Python module或package

    很基础很重要的一课,虽然很简单,但是防止以后忘了,还是记下来 这个笔记里说的都是import本地的,自己创建的,或者复制粘贴的别人的,总之“不是安装到library”的module or packag ...

  8. python module的结构

    python有很多module,下面是module的结构图: 拿httplib做例子,httlip module有: 4个class( HTTPConnection,HTTPSConnection,H ...

  9. python module install

    1.issue: How can I bypass kivy module error: ImportError: DLL load failed: The specified module coul ...

随机推荐

  1. 关于echarts

    昨天随手玩了下echarts,看见同事纠结于echarts的兼容问题. 最简单的echarts(官网的): <div id="main" style="width: ...

  2. Rwordseg使用

    #用于下载安装rJava 和 Rwordseg,如果安装了就注释掉 install.packages("rJava") install.packages("Rwordse ...

  3. Linux显示inode的信息

    Linux显示inode的信息 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ df -i 文件系统 Inode 已用(I) 可用(I) 已用(I)% 挂载点 ...

  4. Struts2(四)Struts2配置文件的配置

    Struts2的常见配置 1.Struts2的配置文件的加载顺序: 每次从客户端发送到请求到服务器都要先从Struts2的核心过滤器StrutsPrepareAndExeccuteFilter,这个过 ...

  5. R语言实现对基因组SNV进行注释

    很多时候,我们需要对取出的SNV进行注释,这个时候可能会在R上进行注释,通常注释文件都含有Chr(染色体).Start(开始位点).End(结束位点).Description(描述),而我们的SNV文 ...

  6. monkeyrunner_获取apk的包名和activity名

    一.使用adb获取单个apk的包名和Activity名称: 1.配置adb环境 a. 我的电脑点击右键-属性-高级-环境变量; b.  环境变量中新建PATH,变量值输入adb.exe工具所在目录; ...

  7. 【转】Nginx的启动、停止与重启

    Nginx的启动.停止与重启 启动 启动代码格式:nginx安装目录地址 -c nginx配置文件地址 例如: [root@LinuxServer sbin]# /usr/local/nginx/sb ...

  8. JS实现回到Top(顶部)--JavaScript

    当我们浏览一段很长的网页时,已经看到底部了,想回到顶部看前面的内容,可是需要滚动好几转鼠标滑轮或者拉动滚动条走好长“一段路”.这对于用户来说,体验效果是不够好的.如果我们借助简单的一个按钮,点击一下就 ...

  9. js 数组去重常见的几种方式

    1.利用标记 var arr = [2,6,2,6,4,3,16];// arr = [2,6,4,3,16] function norepeat(arr){ var res = []; for(va ...

  10. order by group by

    order by 后 group by连用, mysql好像 >5.4不起作用 通过 explain 查看执行计划,可以看到没有 limit 的时候,少了一个 DERIVED 操作 估计是内部优 ...