6个常见Python 模块问题解答

原创 tiangr  2016-11-16 23:28  阅读 144 次

1.模块导入的搜索路径和路径搜索

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

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

发生这样的错误时, 解释器会告诉你它无法访问请求的模块, 可能的原因是模块不在搜索路径里, 从而导致了路径搜索的失败。

默认搜索路径是在编译或是安装时指定的。 它可以在一个或两个地方修改。一个是启动 Python 的 shell 或命令行的 PYTHONPATH 环境变量。 该变量的内容是一组用冒号分割的目录路径。 如果你想让解释器使用这个变量, 那么请确保在启动解释器或执行 Python 脚本前设置或修改了该变量。

解释器启动之后, 也可以访问这个搜索路径, 它会被保存在 sys 模块的 sys.path 变量里。不过它已经不是冒号分割的字符串, 而是包含每个独立路径的列表。下面是一个 Unix 机器搜索路径的样例。切记, 搜索路径在不同系统下一般是不同的。

  1. >>> sys.path
  2. ['', '/usr/local/lib/python2.x/', '/usr/local/lib/ python2.x/plat-sunos5',
  3. '/usr/local/lib/python2.x/ lib-tk', '/usr/local/lib/python2.x/lib-dynload', '/
  4. usr/local/lib/Python2.x/site-packages',]

这只是个列表, 所以我们可以随时随地对它进行修改。 如果你知道你需要导入的模块是什么,而它的路径不在搜索路径里, 那么只需要调用列表的 append() 方法即可, 就像这样:

  1. sys.path.append('/home/wesc/py/lib')

修改完成后, 你就可以加载自己的模块了。 只要这个列表中的某个目录包含这个文件, 它就会被正确导入。 当然, 这个方法是把目录追加在搜索路径的尾部。 如果你有特殊需要, 那么应该使用列表的 insert() 方法操作 。 上面的例子里, 我们是在交互模式下修改 sys.path 的, 在脚本程序中也完全可以达到同样的目的。这里是使用交互模式执行时遇到的错误

  1. >>> import sys
  2. >>> import mymodule
  3. Traceback (innermost last): File "<stdin>", line 1, in ?
  4. ImportError: No module named mymodule
  5. >>>
  6. >>> sys.path.append('/home/wesc/py/lib')
  7. >>> sys.path
  8. ['', '/usr/local/lib/python2.x/', '/usr/local/lib/
  9. python2.x/plat-sunos5', '/usr/local/lib/python2.x/
  10. lib-tk', '/usr/local/lib/python2.x/lib-dynload', '/usr/
  11. local/lib/python2.x/site-packages’,'/home/wesc/py/lib']
  12. >>>
  13. >>> import mymodule
  14. >>>

从另一方面看, 你可能有一个模块的很多拷贝。 这时, 解释器会使用沿搜索路径顺序找到的第一个模块。使用 sys.modules 可以找到当前导入了哪些模块和它们来自什么地方。 和 sys.path 不同,sys.modules 是一个字典, 使用模块名作为键( key) , 对应物理地址作为值( value )。

2.名称空间与变量作用域比较

名称空间是纯粹意义上的名字和对象间的映射关系, 而作用域还指出了从用户代码的哪些物理位置可以访问到这些名字。

注意每个名称空间是一个自我包含的单元。但从作用域的观点来看, 事情是不同的. 所有局部名称空间的名称都在局部作用范围内。局部作用范围以外的所有名称都在全局作用范围内。

还要记得在程序执行过程中, 局部名称空间和作用域会随函数调用而不断变化, 而全局名称空间是不变的。

在遇到名称空间的时候想想"它存在吗?", 遇到变量作用域的时候想想"我能看见它吗?"

3.被导入到导入者作用域的名字

只从模块导入名字的另一个副作用是那些名字会成为局部名称空间的一部分。 这可能导致覆盖一个已经存在的具有相同名字的对象。 而且对这些变量的改变只影响它的局部拷贝而不是所导入模块的原始名称空间。 也就是说, 绑定只是局部的而不是整个名称空间。

这里我们提供了两个模块的代码: 一个导入者, impter.py , 一个被导入者, imptee.py 。impter.py 使用 from-import 语句只创建了局部绑定。

  1. #############
  2. # imptee.py #
  3. #############
  4. foo = 'abc'
  5. def show():
  6. print 'foo from imptee:', foo
  7. #############
  8. # impter.py #
  9. #############
  10. from imptee import foo, show
  11. show()
  12. foo = 123
  13. print 'foo from impter:', foo
  14. show()

运行这个导入者程序, 我们发现从被导入者的观点看, 它的 foo 变量没有改变, 即使 我们在 importer.py 里修改了它。

  1. foo from imptee: abc
  2. foo from impter: 123
  3. foo from imptee: abc

唯一的解决办法是使用 import 和完整的标识符名称(句点属性标识)。

  1. #############
  2. # impter.py #
  3. #############
  4. import imptee
  5. imptee.show()
  6. imptee.foo = 123
  7. print 'foo from impter:', imptee.foo
  8. imptee.show()

完成相应修改后, 结果如我们所料:

  1. foo from imptee: abc
  2. foo from impter: 123
  3. foo from imptee: 123

4.包

包是一个有层次的文件目录结构, 它定义了一个由模块和子包组成的 Python 应用程序执行环境。

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

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 等是它的子包。 我们可以这样导入子包:

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

你也可使用 from-import 实现不同需求的导入。
第一种方法是只导入顶层的子包, 然后使用属性/点操作符向下引用子包树:

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

此外, 我们可以还引用更多的子包:

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

事实上, 你可以一直沿子包的树状结构导入:

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

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

5. 绝对导入 和 相对导入

包的使用越来越广泛, 很多情况下导入子包会导致和真正的标准库模块发生(事实上是它们的名字)冲突。 包模块会把名字相同的标准库模块隐藏掉, 因为它首先在包内执行相对导入, 隐藏掉标准库模块。为 此 , 所 有 的 导 入 现 在 都 被 认 为 是 绝 对 的 , 也 就 是 说 这 些 名 字 必 须 通 过 Python 路 径(sys.path 或是 PYTHONPATH )来访问。

绝对导入特性限制了模块作者的一些特权。失去了 import 语句的自由, 必须有新的特性来满足程序员的需求。这时候, 我们有了相对导入。 相对导入特性稍微地改变了 import 语法, 让程序员告诉导入者在子包的哪里查找某个模块。因为 import 语句总是绝对导入的, 所以相对导入只应用于 from-import 语句。

语法的第一部分是一个句点, 指示一个相对的导入操作。 之后的其他附加句点代表当前 from起始查找位置后的一个级别。

再来看看上边的例子。在 Analog.Mobile.Digital , 也就是 Digital.py 模块中, 我们不能简单地使用这样的语法。 下边的代码只能工作在旧版本的 Python 下, 在新的版本中它会导致一个警告, 或者干脆不能工作:

  1. import Analog
  2. from Analog import dial

这是绝对导入的限制造成的。你需要在使用绝对导入或是相对导入中做出选择。下边是一些可行的导入方法:

  1. from Phone.Mobile.Analog import dial
  2. from .Analog import dial
  3. from ..common_util import setup
  4. from ..Fax import G3.dial

5. Python import同级module调用

http://85608547.blog.51cto.com/2093443/1576759

看了一段Python的基础视频,正好赶上单位需要做个小工具。索性拿它练练手,刚刚开动就遇到一个新的问题:目录引用。简单的说,就是将不同的功能代码,分到不同的目录文件中,代码中涉及到同级目录调用,问题就出来了“SystemError: Parent module '' not loaded, cannot perform relative import”。
先看一下目录结构:

222

OperateExcel.py

1
2
3
4
5
6
7
#!F:/Python/python
from ..Model.ObjectFile import *
def showMessage():
    print('OperateExcel file is Load')

ObjectFile.py

1
2
def showMessage():
    print('Object File is running!')

在python环境调用提示:

Traceback (most recent call last):

File "Operate/OperateExcel.py", line 4, in <module>

from ..Model.ObjectFile import *

SystemError: Parent module '' not loaded, cannot perform relative import

查阅了一些资料,发现问题在于引用路径上,就是OperateExcel.py 的

1
from ..Model.ObjectFile import *

修改OperateExcel.py 查看一下,该文件在启动时加载了哪些路径。

修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#!F:/Python/python
# from ..Model.ObjectFile import *
# def showMessage():
#  print('OperateExcel file is Load')
import sys,pprint
pprint.pprint(sys.path)

打印输出默认调用路径:

['F:\\Python\\Project\\ExcelOperateProject\\Operate',

'C:\\Windows\\system32\\python33.zip',

'C:\\Python33\\DLLs',

'C:\\Python33\\lib',

'C:\\Python33',

'C:\\Python33\\lib\\site-packages']

问题貌似明朗化,加载时未找到加载的路径。曾经尝试修改Operate目录下的__init__.py文件(该文件默认为空)。

1
2
3
4
import sys
sys.pate.append('F:\\Python\\Project\\ExcelOperateProject\\Model',
 
)

调用OperateExcel.py输入如下:

['F:\\Python\\Project\\ExcelOperateProject\\Operate',

'C:\\Windows\\system32\\python33.zip',

'C:\\Python33\\DLLs',

'C:\\Python33\\lib',

'C:\\Python33',

'C:\\Python33\\lib\\site-packages']

(对于__init__还停留在初级阶段,如有使用不当还请批评指正)。

问题原因已经明确,即文件调用未加载指定路径,造成编译错误。经过度娘查询一些以往大神的经验。发现主要问题在于同级module无法直接调用。需要先找到上一级的module而后查询到调用的内容。前提你的执行文件目录不能与你的引入文件同级。所以我修改一下目录结构如下:

wKioL1RmzWCwxHhgAACPBAuBq8c551.jpg

将Model目录、Operate目录封装到package目录下,并且在package目录下创建空的__init__.py文件(这个很重要,否则在跨模块调用时会提示错误,因编译程序未将package看作是module)。然走在ExcelOperateProject目录下建立main.py调用package/Operate/OperateExcel.py代码如下:

1
2
3
from package.Operate import OperateExcel
OperateExcel.showMessage()
特别提示:本站资源全部免费下载,因服务器需经费维护,文中部分外链点击后会进入广告,请耐心等待5秒即可跳过广告进入目标页面。如遇页面外链打不开或下载地址失效,您可以在评论中指出错误,或扫描页面底部二维码。
本文地址:http://www.tiangr.com/6-ge-chang-jian-python-mo-kuai-wen-ti-jie-da.html
版权声明:本文为原创文章,版权归 tiangr 所有,欢迎分享本文,转载请保留出处!

发表评论


表情