12.8 模块的其他特性
12.8.1 自动载入的模块
当Python解释器在标准模式下启动时,一些模块会被解释器自动导入,用于系统相关操作。唯一一个影响你的是builtin模块,它会正常地被载入,这和builtins模块相同。
sys.modules变量包含一个由当前载入(完整且成功导入)到解释器的模块组成的字典,模块名作为键,它们的位置作为值。
例如在Windows下,sys.modules变量包含大量载入的模块,我们这里截短它,只提供他们的模块名,通过调用字典的keys()方法:
Unix下载入的模块很类似:
12.8.2 阻止属性导入
如果你不想让某个模块属性被“from module import*”导入,那么你可以给你不想导入的属性名称加上一个下划线(_)。不过如果你导入了整个模块或是你显式地导入某个属性(例如import foo._bar),这个隐藏数据的方法就不起作用了。
12.8.3 不区分大小的导入
有一些操作系统的文件系统是不区分大小写的。Python 2. 1前,Python尝试在不同平台下导入模块时候“做正确的事情”,但随着MacOSX和Cygwin平台的流行,这样的不足已经不能再被忽视,而需要被清除。
在Unix(区分大小写)和Win32(不区分大小写)下,一切都很明了,但那些新的不区分大小写的系统不会被加入区分大小写的特性。PEP 235指定了这个特性,尝试解决这个问题,并避免那些其他系统上“hack”式的解决方法。底线就是为了让不区分大小写的导入正常工作,必须指定一个叫做PYTHONCASEOK的环境变量。Python会导入第一个匹配模块名(使用不区分大小写的习惯)。否则Python会执行它的原生区分大小写的模块名称匹配,导入第一个匹配的模块。
12.8.4 源代码编码
从Python 2.3开始,Python的模块文件开始支持除7位ASCII之外的其他编码。当然ASCII是默认的,你只要在你的Python模块头部加入一个额外的编码指示说明就可以让导入者使用指定的编码解析你的模块,编码对应的Unicode字符串。所以你使用纯ASCII文本编辑器的时候不需要担心了(不需要把你的字符串放入“Unicode标签”里)。
一个UTF-8编码的文件可以这样指示:
如果你执行或导入了包含非ASCII的Unicode字符串而没有在文件头部说明,那么你会在Python 2. 3得到一个DeprecationWaming,而在2. 5中这样做会导致语法错误。你可以在PEP 263中得到更多关于源文件编码的相关内容。
12.8.5 导入循环
实际上,在使用Python时,你会发现是能够导入循环的。如果你开发了大型的Python工程,那么你很可能会陷入这样的境地。
我们来看一个例子。假定我们的产品有一个很复杂的命令行接口(command-line interface, CLI) 。其中将会有超过一百万的命令,结果你就有了一个“超冗余处理器”(overly massive handler, OMH)子集。每加入一个新特性,将有1〜3条的新命令加入,用于支持新的特性。下边是我们的omh4cli.py脚本:
假定大多控制器都要用到这里的(其实是空的)工具函数。命令行接口的OMH都被封装在omh4cli()函数里。如果我们要添加一个新的命令,那么它会被调用。
现在这个模块不断地增长,一些聪明的工程师会决定把新命令放入到隔离的模块里,在原始模块中只提供访问新东西的钩子。这样,管理代码会变得更简单,如果在新加入内容中发现了bug,那么你就不必在一个几兆的Python文件里搜索。
在我们的例子中,有一个兴奋的经理要我们加入一个“非常好的特性”。我们将创建一个新的cli4vof.py脚本,而不是把新内容集成到omh4cli.py里:
前边已经提到,工具函数是每个命令必须的,而且由于不能把代码从主控制器复制出来,所以我们导入了主模块,在我们的控制器中添加对omh, omh4cli()的调用。
问题在于主控制器omh4cli会导入我们的cli4vof模块(获得新命令的函数),而cli4vof也会导入omh4cli(用于获得工具函数)。模块导入会失败,这是因为Python尝试导入一个先前没有完全导入的模块:
注意跟踪记录中显示的对cli4vof的循环导入。问题在于要想调用工具函数,cli4vof必须导入omh4cli。如果它不需要这样做,那么omh4cli将会成功导入cli4vof,程序正常执行。但在这里,omh4cli尝试导入cli4vof,而cli4vof也试着导入omh4cli。最后谁也不会完成导入工作,引发错误。这只是一个导入循环的例子。事实上实际应用中会出现更复杂的情况。
解决这个问题几乎总是移除其中一个导入语句。你经常会在模块的最后看到import语句。作为一个初学者,你只需要试着习惯它们,如果你以前遇到在模块底部的import语句,现在你知道是为什么了。在我们的例子中,我们不能把import omh4cli移到最后,因为调用cli4vof()的时候omh4cli()名字还没有被载入。
我们的解决方法只是把import语句移到cli4vof()函数内部:
这样,从omh4cli()导入cli4vof()模块会顺利完成,在omh4cli()被调用之前它会被正确导入。只有在执行到cli4vof.cli4vof()时候才会导入omh4cli模块。
12.8.6 模块执行
有很多方法可以执行一个Python模块:通过命令行或shell、execfile()、模块导入、解释器的-m选项等。这已经超出了本章的范围。你可以参考第14章,里边全面地介绍了这些特性。