python模块

模块

       模块支持从逻辑上组织 Python 代码。 当代码量变得相当大的时候, 我们最好把代码分成一些有组织的代码段,前提是保证它们的彼此交互。 这些代码片段相互间有一定的联系, 可能是一个包含数据成员和方法的类, 也可能是一组相关但彼此独立的操作函数。 这些代码段是共享的,所以Python 允许 “调入” 一个模块, 允许使用其他模块的属性来利用之前的工作成果, 实现代码重用.这个把其他模块中属性附加到你的模块中的操作叫做导入(import) 。那些自我包含并且有组织的代码片断就是模块。

模块和文件

       如果说模块是按照逻辑来组织 Python 代码的方法, 那么文件便是物理层上组织模块的方法。因此, 一个文件被看作是一个独立模块, 一个模块也可以被看作是一个文件。 模块的文件名就是模块的名字加上扩展名 .py 。这里我们需要讨论一些关于模块文件结构的问题。 与其它可以导入类(class)的语言不同,在 Python 中你导入的是模块或模块属性。

搜索路径和路径搜索

       模块的导入需要一个叫做”路径搜索”的过程。 即在文件系统”预定义区域”中查找 mymodule.py文件(如果你导入 mymodule 的话)。 这些预定义区域只不过是你的 Python 搜索路径的集合。路径搜索和搜索路径是两个不同的概念, 前者是指查找某个文件的操作, 后者是去查找一组目录。 有时候导入模块操作会失败:

>>> import xxx
Traceback (innermost last):
File "<interactive input>", line 1, in ?
ImportError: No module named xxx

发生这样的错误时, 解释器会告诉你它无法访问请求的模块, 可能的原因是模块不在搜索路径里, 从而导致了路径搜索的失败。默认搜索路径是在编译或是安装时指定的。 它可以通过以下方式修改:
1.一个是启动 Python 的 shell 或命令行的 PYTHONPATH 环境变量。 该变量的内容是一组用冒号分割的目录路径。 如果你想让解释器使用这个变量, 那么请确保在启动解释器或执行 Python 脚本前设置或修改了该变量。
2.解释器启动之后, 也可以访问这个搜索路径, 它会被保存在 sys 模块的 sys.path 变量里。不过它已经不是冒号分割的字符串, 而是包含每个独立路径的列表。我们随时对这个列表进行修改,只需要调用列表的 append() 方法即可:

>>> sys.path
['', 'C:\\Windows\\SYSTEM32\\python34.zip', 'F:\\Program Files\\python34\\DLLs', 'F:\\Program Files\\python34\\lib', 'F:\\Program Files\\python34', 'F:\\Program Files\\python34\\lib\\site-packages']
>>>sys.path.append('F:\\mylib')
>>>sys.path
['', 'C:\\Windows\\SYSTEM32\\python34.zip', 'F:\\Program Files\\python34\\DLLs', 'F:\\Program Files\\python34\\lib', 'F:\\Program Files\\python34', 'F:\\Program Files\\python34\\lib\\site-packages', 'F:\\mylib']

修改完成后, 你就可以加载自己的模块了。 只要这个列表中的某个目录包含这个文件, 它就会被正确导入。 当然, 这个方法是把目录追加在搜索路径的尾部。 如果你有特殊需要, 可以使用列表的 insert() 方法操作 。
注意:你可能在多个路径下有多个同名的模块,这时,解释器会使用沿搜索路径顺序找到的第一个模块。
使用 sys.modules 可以找到当前导入了哪些模块和它们来自什么地方。 和 sys.path 不同,sys.modules 是一个字典, 使用模块名作为键( key) , 对应物理地址作为值( value )。

命名空间

       名称空间是名称(标识符)到对象的映射。 向名称空间添加名称的操作过程涉及到绑定标识符到指定对象的操作(以及给该对象的引用计数加 1 )。《Python 语言参考》(Python Language Reference)有如下的定义: 改变一个名字的绑定叫做重新绑定, 删除一个名字叫做解除绑定。
       python程序在执行期间有两个或三个活动的名称空间。 这三个名称空间分别是局部名称空间, 全局名称空间和内建名称空间, 但局部名称空间在执行期间是不断变化的, 所以我们说”两个或三个”。 从名称空间中访问这些名字依赖于它们的加载顺序, 或是系统加载这些名称空间的顺序。
       Python 解释器首先加载内建名称空间。 它由 builtins 模块中的名字构成。 随后加载执行模块的全局名称空间, 它会在模块开始执行后变为活动名称空间。 这样我们就有了两个活动的名称空间。如果在执行期间调用了一个函数, 那么将创建出第三个名称空间, 即局部名称空间。 我们可以通过 globals() 和 locals() 内建函数判断出某一名字属于哪个名称空间。

名称查找与变量作用域

名称空间是纯粹意义上的名字和对象间的映射关系, 而作用域还指出了从用户代码的哪些物理位置可以访问到这些名字。访问一个属性时, 解释器必须在三个名称空间中的一个找到它。 首先从局部名称空间开始, 如果没有找到, 解释器将继续查找全局名称空间. 如果这也失败了, 它将在内建名称空间里查找。 如果最后的尝试也失败了, 你会得到这样的错误:

>>> xxx
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xxx' is not defined

局部名称空间中的名字会隐藏全局或内建名称空间的对应对象。 这就相当于”覆盖”了那个全局变量。

>>> def f():
...     a=100
...     print(a)
...
>>> a=200
>>> f()
100

无限制名称空间

       Python 的一个有用的特性在于你可以在任何需要放置数据的地方获得一个名称空间。你可以把任何想要的东西放入一个名称空间里。

>>> def f():                   
...     pass                   
...                           
>>> f.version=1                
>>> f.name="f"                 
>>> print(f.version,f.name)    
1 f                            

导入模块

import语句

       使用 import 语句导入模块, 它的语法如下所示:

import module1
import module2
:
import moduleN

也可以在一行内导入多个模块,但是这样的代码可读性不如多行的导入语句,最好一行只导入一个模块。

import module1,module2,module3,module4

导入模块的最佳顺序:Python 标准库模块、Python 第三方模块、应用程序自定义模块,解释器执行到这条语句, 如果在搜索路径中找到了指定的模块, 就会加载它。该过程遵循作用域原则, 如果在一个模块的顶层导入, 那么它的作用域就是全局的; 如果在函数中导入, 那么它的作用域是局部的。如果模块是被第一次导入, 它将被加载并执行。

from-import 语句

可以在模块里导入指定的模块属性。 也就是把指定名称导入到当前作用域。 使用from-import 语句可以实现我们的目的, 语法格式:

from module import name1, name2,... nameN

之后使用导入的属性就不必加模块名了:

>>> from os import system
>>> system("ls")

模块导入的特性

加载模块时执行模块

       加载模块会导致这个模块被”执行”。 也就是被导入模块的顶层代码将直接被执行。 这通常包括设定全局变量以及类和函数的声明。 如果有检查 name 的操作, 那么它也会被执行。一个模块只被加载一次, 无论它被导入多少次。 这可以阻止多重导入时代码被多次执行。 例如你的模块导入了 sys 模块, 而你要导入的其他 5 个模块也导入了它, 那么每次都加载 sys (或是其他模块)不是明智之举! 所以, 加载只在第一次导入时发生。

相关内建函数

globals() 和 locals() 内建函数

       globals() 和 locals() 内建函数分别返回调用者全局和局部名称空间的字典。 在一个函数内部, 局部名称空间代表在函数执行时候定义的所有名字, locals() 函数返回的就是包含这些名字的字典。 globals() 会返回函数可访问的全局名字。在全局名称空间下, globals() 和 locals() 返回相同的字典, 因为这时的局部名称空间就是全局空间。

reload()内建函数

reload() 内建函数可以重新导入一个已经导入的模块。 它的语法如下: reload(module)
module 是你想要重新导入的模块。使用 reload() 的时候有一些标准。 首先模块必须是全部导入(不是使用 from-import), 而且它必须被成功导入。另外 reload() 函数的参数必须是模块自身而不是包含模块名的字符串。 也就是说必须类似 reload(sys) 而不是 reload(‘sys’)。模块中的代码在导入时被执行, 但只执行一次. 以后执行 import 语句不会再次执行这些代码,只是绑定模块名称。 而 reload() 函数不同,每次调用reload() 函数,模块中的代码都会被执行。

       包是一个有层次的文件目录结构, 它定义了一个由模块和子包组成的 Python 应用程序执行环境。Python 1.5 加入了包, 用来帮助解决如下问题:
1、为平坦的名称空间加入有层次的组织结构
2、允许程序员把有联系的模块组合到一起
3、允许分发者使用目录结构而不是一大堆混乱的文件
3、帮助解决有冲突的模块名称
与类和模块相同, 包也使用句点属性标识来访问他们的元素。 使用标准的 import 和from-import 语句导入包中的模块。

目录结构

假定我们的包有如下的目录结构:

Phone/
        __init__.py
        common_util.py
        Voicedta/
                   __init__.py
                   Pots.py
                   Isdn.py
        Fax/
                   __init__.py
                   G3.py
        Mobile/
                   __init__.py
                   Analog.py
                   igital.py
        Pager/
                   __init__.py
                   Numeric.py

Phone 是最顶层的包, Voicedta 等是它的子包。 我们可以这样导入子包:

import Phone.Mobile.Analog
Phone.Mobile.Analog.dial()

也可使用 from-import 导入子包:
注意:如果是较新版本的python,下列导入会有问题,详见下面绝对导入和相对导入

from Phone import Mobile
from Phone.Mobile import Analog
Mobile.Analog.dial('555-1212')
Analog.dial('555-1212')

在我们上边的目录结构中, 我们可以发现很多的 init.py 文件。 这些是初始化模块,from-import 语句导入子包时需要用到它。 如果没有用到, 他们可以是空文件。 程序员经常忘记为 它 们 的 包 目 录 加 入 init.py 文 件 , 所 以 从 Python 2.5 开 始 , 这 将 会 导 致 一 个ImportWarning 信息。

绝对导入

包的使用越来越广泛, 很多情况下导入子包会导致和真正的标准库模块发生(事实上是它们的名字)冲突。 包模块会把名字相同的标准库模块隐藏掉, 因为它首先在包内执行相对导入, 隐藏掉标准库模块。为 此 , 所 有 的 导 入 现 在 都 被 认 为 是 绝 对 的 , 也 就 是 说 这 些 名 字 必 须 通 过 Python 路 径(sys.path 或是 PYTHONPATH )来访问。这个决定的基本原理是子包也可以通过 sys.path 访问, 例如 import Phone.Mobile.Analog ,如果 Mobile不在Python 路 径中,从 Mobile 子包内模块中导入 Analog就会出问题。作为一个折中方案, Python 允许通过在模块或包名称前置句点实现相对导入。

相对导入

绝对导入特性限制了模块作者的一些特权。失去了 import 语句的自由, 必须有新的特性来满足程序员的需求。于是就有了相对导入。 相对导入特性稍微地改变了 import 语法, 让程序员告诉导入者在子包的哪里查找某个模块。因为 import 语句总是绝对导入的, 所以相对导入只应用于 from-import 语句。语法的第一部分是一个句点, 指示一个相对的导入操作。 之后的其他附加句点代表当前 from起始查找位置后的一个级别。

from Phone.Mobile.Analog import dial
from .Analog import dial
from ..common_util import setup
from ..Fax import G3.dial.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值