首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Python:Module

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

1 Module 组成

1.1 Module 内置全局变量

2 Module 导入

2.1 导入及其使用

2.2 一次加载多次导入

2.3 搜索路径

2.4 reload

3 Package

3.1 __init__.py

3.2 __all__

3.3 __path__

1 Module组成

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

在一个ModuleTests.py文件中:

View Code

除了你自己定义的那些全局变量和函数外,每一个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

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

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

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

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包来做实验,由于该文件比较大,我就写出主要部分:

目录结构如下:

当程序中使用 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 encoderimport jsonprint(json.encoder == encoder)print(json.decoder is None)print(json.scanner is None)print(dir())

#!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 sysimport osprint "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的功能

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180324A1C1MZ00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券