要点概论:
1.引言
2. 用模块建立抽象层
3. 管理模块名称
4. 设置 PTH 文件(待补充)
5.附录
1. 引言
python是个支持多范式的程序设计语言,无论采取过程式,函数式或面向对象,在构建程序时都应该思考以下几个重点。
1)抽象层的封装与隔离
2)对象的状态
3)命名空间
4)资源实体的组织方式,像源码文件,软件包等
在python中,模块也提供了一种抽象层的封装与隔离,下面来了解以下如何善用模块建立最自然的抽象层
2.用模块建立抽象层
一个 .py 文件就是一个模块,这使得模块称为python中最自然的抽象层。想要直到一个模块中有哪些名称,可以使用 dir() 函数。
实际上,当 import 某个模块而使得指定的 .py 文件被加载时,python 解释器会为它创建一个 module 实例,并创建一个模块名称来引用它,
dir() 实际上时查询模块名称引用的 module 实例上有哪些属性(Attribute)名称可以存取。
PS:若调用 dir() 时未指定任何 module 实例,则会插叙当前所在模块的 module 实例上拥有的名称。
下面我们来设计一个银行业务相关的简单程序,并把它定义在一个 bank.py 文件中:

# bank.py def account(name,number,balance): return {'name':name,'number':number,'balance':balance} def deposit(acct,amount): if amount <= 0: print('存款金额不能为负') else: acct['balance'] += amount def withdraw(acct,amount): if amount > acct['balance']: print('金额不足') else: acct['balance'] -= amount def desc(acct): return 'Account:' + str(acct)
接下来再其他的 py 文件中 import bank:

import bank acct = bank.account('zhaoyi','123-4567',1000) bank.deposit(acct,500) bank.withdraw(acct,200) print(bank.desc(acct)) # Account:{'number': '123-4567', 'name': 'zhaoyi', 'balance': 1300}
bank 这个名称不单只是用来避免命名空间的冲突,也时作为一种组织与思考相关功能的方式。
3. 管理模块名称
大部分情况下模块都会被拿来作为命名空间。下面介绍一些引用模块的方式:
1) from import 名称管理
from import 变将被导入模块中名称引用的值赋给当前模块中创建的新名称,
例如,在 foo.py 中定义了一个 x 变量:

x = 10
在另一个 main.py 文件中执行 from foo import x ,实际上会在 main 模块中创建一个 x 变量然后将 foo 中 x 的值 10 赋值给 main 中的 x 变量:

from foo import x print(x) # 10 x = 20 print(x) #20 import foo print(foo.x) #10
简单来说,当执行 from foo import x 时,就是在模块中创建了新变量,而不是使用原本的 foo.x 变量,只是一开始两个变量引用同一个值。
但是,若引用了可变动对象,就要特别小心了。
例如,在foo.py中定义一个 l1 = [10,20]:

l1 = [10,20]

from foo import l1 print(l1) #[10, 20] l1[0] = 15 print(l1) #[15, 20] import foo print(foo.l1) #[15, 20]
这是因为 l1 变量与 foo.l1 都引用了同一个列表(list)对象,因此通过 l1 变量修改索引 0 的元素使用 foo.l1 就会获取修改后的结果。
2)限制 from import *
在当前模块中,如果有些变量不想被 from import *创建同名变量(避免命名空间混乱),有两种方法,
第一种方法用下划线作为开头,例如:

x = 10 l1 = [10,20] _y = 20

from foo import * print(x) #10 print(l1) #[10,20] print(_y) # NameError: name '_y' is not defined
另一种方法是定义一个 __all__ 列表,使用字符串列处可被 from import * 的名称,例如:

__all__ = ['x','l1'] x = 10 l1 = [10,20] _y = 20 z = 30

from foo import * print(x) #10 print(l1) #[10,20] print(z) # NameError: name 'z' is not defined
需要注意的是:无论是下划线开头,还是未被列入 __all__ 列表的名称,只是限制不被 from import *,如果用户 import foo,那么依旧可以使用 foo.x 或 foo._y来存取。

import foo print(foo.x) # 10 print(foo._y) # 20
3)del 模块名称
del 可以将已创建的变量删除。
被 import 的模块名称或者 from import 创建的名称实际上就是个变量,因此可以使用 del 将模块名称或者 from import 的名称删除。
例如:

import foo print(foo.x) # 10 del foo print(foo.x) # name 'foo' is not defined from foo import x print(x) # 10 del x print(x) # name 'x' is not defined
如果想模拟 import foo as qoo,那么可以如下方法实现:

import foo qoo = foo del foo print(qoo.x) # 10 print(foo.x) # name 'foo' is not defined
4)sys.modules
del 用来删除指定的名称而不是删除名称引用的对象本身。
举例来说:

l1 = [1,2] l2 = l1 del l1 print(l2) # [1, 2] print(l1) # name 'l1' is not defined
在上例中,虽然执行了 del l1,然而 l2还是引用着列表实例。
也就是说,del 只是将该名称删除,而不是删除 module 实例。
想要知道当前已加载的 module 名称与实例有哪些,可以通过 sys.modules 【这是个字典对象,键的部分是模块名称,值的部分是 module 实例】,例如:

import sys import foo print('foo' in sys.modules) # True print(foo.x) # 10 del foo print(foo.x) # name 'foo' is not defined print(sys.modules['foo']) # 10
上面的例子可以看到, del foo 删除了 foo名称,然而,我们还是可以通过 sys,module['foo'] 存取到 foo 原来引用的 module 实例。
5)模块名称的作用域
实际上引用模块可以出现在语句能出现的位置,例如 if...else区块或者函数之中,因此能根据不同的情况进行不同的 import。
当使用 import,import as,from import 时,创建的名称其实就是变量名称,因此根据使用 import,import as,from import 的位置所创建的名称也会有其作用域。例如:

def foo(): import foo print(foo.x) foo() # 10 print(foo.x) # AttributeError: 'function' object has no attribute 'x'
4. 设置 PTH 文件(待补充)
5. 附录
一个模块被 import 时发生的事情:
1) 在 sys.path 寻找模块
2) 加载,编译模块的程序代码
3) 创建空的模块对象
4) 在 sys,modules 中记录该模块
5) 执行模块中的程序代码及相关定义