模块是用来组织 Python 代码的方法,而包则是用来组织模块的。

当代码量很大时,我们一般会把代码分成几个有组织的代码段,然后每个代码段之间有一定的联系。代码单之间是共享的,所以Python允许调入一个模块,允许使用其他模块的属性利用之前的工作成果,实现代码重用。那些自我包含并且有组织的代码片段就是模块(module),将其他模块中属性附加到你的模块中的操作较导入(import)

模块是逻辑上的说法,而它们在物理层是一个个独立的文件,模块的文件名就是模块的名字加拓展名.py。与其他可以导入类的语言不同,Python中你导入的是模块或模块属性。

模块的名称空间
模块名称是他们属性名称中的一个重要部分,给定一个模块名后,只可能有一个模块被导入到Python解释器中,所以不同模块间不能出现名称交叉现象。如果在模块mymodule里创建了一个atoi()函数,那么它的名字应该是 mymodule.atoi()。所以即使属性之间有名称冲突,但他们的完整授权名称通过句点属性标识指定了各自的名称空间,防止名称冲突产生。

模块的导入需要一个路径搜索的过程。即在文件的“预定义区域”中查找 mymodule.py 文件(如果导入的是mymodule的话)。这些预定义区域不过是你的 Python 搜索路径的集合。路径搜索和搜索路径概念不同,前者指查找某个文件,后者是去查找一组目录。
默认的搜索路径是在编译或是安装时指定的,它可以在一个或两个地方修改。
一个是启动Python的shell命令行的PYTHONPATH环境变量。该变量的内容是一组用冒号分隔的目录路径。如果你想让解释器使用这个变量,请确保在启动解释器或执行Python脚本前设置或修改了该变量。
解释器启动之后,也可以访问这个搜索路径,它会被保存在 sys 模块的 sys.path 变量里。
不过它已经不是冒号分隔的字符串,而是包含每个独立路径的列表。
这只是个列表,所以可以随时随地对它修改,如果需要知道你要导入的模块是什么,而它的路径不再搜索路径里,那么只需调用append()方法即可。
修改完成后,你就可以加载自己的模块了。只要列表中的某个目录包含这个而文件,它就会被正确导入。
在同一个模块的多个拷贝都出现在路径中时,会使用沿用搜索路径找到的第一个模块。

名称空间
名称空间是名称(标识符)到对象的映射。向名称空间添加名称的操作过程涉及到绑定标识符到指定对象的操作(以及给该对象的引用计数加1)。
Python在执行期间由两个或三个活动的名称空间,分别是局部名称空间,全局名称空间和内建名称空间。
Python解释器会首先加载内建名称空间,它由 __builtins__ 模块的名字构成。随后加载执行模块的全局名称空间,他会在模块开始执行后变为活动名称空间。如果在执行期间调用了一个函数,那么将创建出第三个名称空间,局部名称空间。

关于__builtins__ 和 __builtin__
现在尽可能只使用 __builtins__ ,不使用__builtin__ ,现在所有__builtin__的内容都在__builtins__里有,而__builtin__已经不太支持。

名称空间与变量作用域的关系
名称空间是纯粹意义上的名字和对象间的映射关系,而作用域还指出了从用户代码的那些物理位置可以访问到这些名字。
名称查找、确定作用域和覆盖
名称查询将确定作用域的规则覆盖到名称空间。访问一个属性时,解释器必须在三个名称空间中的一个里找到它,先从局部名称空间开始,如果没有找到,解释器会继续查找全局名称空间,如果失败了,将在内建名称空间里查找。如果都没找到,返回一个NameError。
这种先后查找的方式是遮蔽了之后的名称空间。也就是局部变量覆盖了全局变量:

def foo():
  print "calling foo()..."
  bar = 200
  print "in foo(), bar is",bar
bar = 100
print "in __main__, bar is", bar
foo()
print "in __main__, bar is", bar

结果是:

>>>
in __main__, bar is 100
calling foo()...
in foo(), bar is 200
in __main__, bar is 100

如果如下代码:

def foo():
  print "calling foo()..."
  print "before we assign bar,bar is",bar
  bar = 200
  print "in foo(), bar is",bar
bar = 100
print "in __main__, bar is", bar
foo()
print "in __main__, bar is", bar

则返回:

>>>
in __main__, bar is 100
calling foo()...
before we assign bar,bar is Traceback (most recent call last):
File "F:/桌面/PythonProgram/namespace.py", line 8, in <module>
foo()
File "F:/桌面/PythonProgram/namespace.py", line 3, in foo
print "before we assign bar,bar is",bar
UnboundLocalError: local variable 'bar' referenced before assignment

如果删除其中一行:

def foo():
  print "calling foo()..."
  print "before we assign bar,bar is",bar
  print "in foo(), bar is",bar
bar = 100
print "in __main__, bar is", bar
foo()
print "in __main__, bar is", bar

则结果为:

>>>
in __main__, bar is 100
calling foo()...
before we assign bar,bar is 100
in foo(), bar is 100
in __main__, bar is 100

上面的尝试里,反映出了局部名称空间覆盖了全局名称空间的过程。当我在函数中给bar赋值后,bar变成局部变量,也就是出现在局部名称空间里,此时在给bar赋值前调用它就会报错。

导入模块
导入语句有两种:

import module1
import module2
...或 import module1,module2,...

这两种没有太大的不同,只有可读性的差别。

核心风格:一般我们推荐所有的模块在开头导入,最好按这样的顺序
Python标准库模块
Python第三方模块
应用程序自定义模块
然后用一个空行分割这三个模块的导入语句

from-import语句
这是导入模块的指定属性。也就是把指定名称导入到当前作用域,语法如下:

from module import name1[,name2,...]

导入内容过长变成多行导入时,用一个\可以换行。

拓展import语句(as)
当你想使用一个模块或者模块属性,但想改一个名字时,可以使用自己要的名字替换模块的原始名字,语法如下:

import longmodulename as yourname

from-import语句可以把名字导入到当前空间去,不需要在使用时加句点属性标识符,如果要把所有的名称都导入到当前名称空间可以用:

from module import *

当然,从实践中来看,这不是很好的编程风格,它污染了当前名称空间,很可能覆盖当前名称空间现有的名字。

模块内建函数
__import__()
这是import的实际函数,我们使用它来完成导入工作,而提供这个函数是为了让有特殊需求的用户来覆盖它,实现自定义导入。
__import__()语法是:

__import__(module_name[,globals[, locals[, fromlist[]]]])

module_name 是导入模块的名字,globals 是包含当前全局符号表的名字的字典, locals 是包含局部符号表的名字的字典, fromlist是一个使用from-import 语句所导入符号的列表。

globals() 和 locals()
这两个内建函数分别返回调用者全局和局部名称空间的字典。
在全局名称空间下,globals() 和 locals() 返回相同的字典,因为局部名称空间就是全局空间。

reload()
这个内建函数可以重新导入一个已经导入的模块。语法是:

reload(module)

模块中的代码在导入时被执行,但只执行一次。以后执行 import 语句不会再次执行这些代码,只是绑定模块名称。而 reload()可以多次执行。


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

假设有这样的包
Phone/
  __init__.py
  common_util.py
  Voicedta/
    __init__.py
    Pots.py
    Isdn.py
  Fax/
    __init__.py
    G3.py
  Mobile/
    __init__.py
    Analog.py
    digital.py
  Pager/
    __init__.py
    Numeric.py
可以这样导入包:

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

from Phone import Mobile
Mobile.Analog.dial()

from Phone.Mobile import Analog
Analog.dial()

from Phone.Mobile.Analog import dial
dial()

上面的目录结构中有很多__init__.py文件。这是初始化模块,导入子包时from-import语句要用到它。如果没有用到,他们可以是空文件。

绝对导入
因为包的使用越来越广发,很多情况下导入子包会和标准库模块发生冲突,包模块会把名字相同的标准库模块隐藏掉。为此,所有的导入现在都被认为是绝对的,也就是说这些名字必须通过Python路径来访问。
当然,也留下了一定的相对导入操作,第一部分是句点,表示相对导入,然后用附加句点表示在哪个位置级别。
如上的目录结构,如果我们在Digital.py中,有以下的导入方法:

from Phone.Mobile.Analog import dial #绝对导入
from .Analog import dial
from ..common_util import setup
form ..Fax import G3.dial

2015/9/15 Python基础(12):模块和包的更多相关文章

  1. 二十五. Python基础(25)--模块和包

    二十五. Python基础(25)--模块和包 ● 知识框架   ● 模块的属性__name__ # my_module.py   def fun1():     print("Hello& ...

  2. python基础(22):模块、包

    1. 模块 1.1 什么是模块 别人写好的函数.变量.方法放在一个文件里 (这个文件可以被我们直接使用)这个文件就是个模块 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模 ...

  3. Python基础之模块与包

    一.模块 1.什么是模块? 一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 2.为何要使用模块? 如果你退出python解释器然后重新进入,那么你之前定义的函 ...

  4. python基础——使用模块

    python基础——使用模块 Python本身就内置了很多非常有用的模块,只要安装完毕,这些模块就可以立刻使用. 我们以内建的sys模块为例,编写一个hello的模块: #!/usr/bin/env ...

  5. 『Python基础-12』各种推导式(列表推导式、字典推导式、集合推导式)

    # 『Python基础-12』各种推导式(列表推导式.字典推导式.集合推导式) 推导式comprehensions(又称解析式),是Python的一种独有特性.推导式是可以从一个数据序列构建另一个新的 ...

  6. python基础——第三方模块

    python基础——第三方模块 在Python中,安装第三方模块,是通过包管理工具pip完成的.  如果你正在使用Mac或Linux,安装pip本身这个步骤就可以跳过了.  如果你正在使用Window ...

  7. 十二. Python基础(12)--生成器

    十二. Python基础(12)--生成器 1 ● 可迭代对象(iterable) An object capable of returning its members one at a time. ...

  8. python 基础之 模块

    Python 基础之模块 一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 就是一个python文件中定义好了类和方法,实现了一些功能,可以被别的python文 ...

  9. python基础之打/解包及运算符与控制流程

    python基础之打/解包及运算符与控制流程 python中的解压缩(即序列类型的打包和解包) python提供了两个设计元祖和其他序列类型的处理的便利,也就是自动打包与自动解包功能,比如: data ...

随机推荐

  1. Git 命令详解及常用命令

    Git 命令详解及常用命令 Git作为常用的版本控制工具,多了解一些命令,将能省去很多时间,下面这张图是比较好的一张,贴出了看一下: 关于git,首先需要了解几个名词,如下: 1 2 3 4 Work ...

  2. Android中的回调Callback

    回调就是外部设置一个方法给一个对象, 这个对象可以执行外部设置的方法, 通常这个方法是定义在接口中的抽象方法, 外部设置的时候直接设置这个接口对象即可. 例如给安卓添加按钮点击事件, 我们创建了OnC ...

  3. 3dContactPointAnnotationTool开发日志(二二)

      昨天是实现了显示GameObject子GameObject的选项卡功能,今天就是要让statusPanel可以控制它们的位置.旋转和缩放了.   没什么难的,对应选项卡绑定上对应的物体或子物体即可 ...

  4. ios::sync_with_stdio(false)提高C++读写速度

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:ios::sync_with_stdio(false)提高C++读写速度     本文地址:h ...

  5. PHPCMSV9 黄页新闻、产品、商机均无法浏览具体信息,显示您没有访问该信息的权限!

    原帖地址:http://bbs.phpcms.cn/forum.php?mod=viewthread&tid=294956&highlight=%C3%BB%D3%D0%B7%C3%C ...

  6. 【转】关于增量链接(incremental linking)

    增量链接(Incremental Linking)这个词语在使用Visual C++时经常会遇到(其实不只是VS系列,其它链接器也有这个特性), 就比如经常遇到的:上一个增量链接没有生成它, 正在执行 ...

  7. Java 8中 基本数据类型

    1)四种整数类型(byte.short.int.long):    byte:8 位,用于表示最小数据单位,如文件中数据,-128~127    short:16 位,很少用,-32768 ~ 327 ...

  8. [剑指Offer] 60.把二叉树打印成多行

    题目描述 从上到下按层打印二叉树,同一层结点从左至右输出.每一层输出一行. [思路]使用队列实现二叉树的层次遍历. /* struct TreeNode { int val; struct TreeN ...

  9. query 获取本身的HTML

    <div class="test"><p>hello,你好!</p></div> <script> $(".t ...

  10. vs code 自动补全效果不理想的问题

    之前一直用webstorm,最近换换口味,改用了VS Code,发现VS Code 智能提示得到的都不是我想要的 就比如  ! + tab ,HTML结构都出不来.经过一番搜索,发现是 VS Code ...