Python import语句一处文档和行为不一致的问题

话说,我团运维组曾一度极为昌盛,可惜后来公司里有人搞政治,好好的一个组被拆成了好几个,后来竟分属不同大部门,工作关系也开始形同陌路。考虑到丰富经验,换换方向,今年7月,我转岗到了运维开发组,开始真正把Python作为职业。

无奈,基础差,还得学,8月18日,这么吉利的一天,我第n次在看Python的文档,看着看着,注意到一些标准库的名字是带点的,比如os.path,再比如logging.config等。然后就开始help各个模块,发现os.path的名字居然不叫path而是posixpath!!再看,logging是个package,但os居然是个module!!

于是,问题就来了:

Python语言里对 import A.B.C 这种形式是这么规定的:

when using syntax like import item.subitem.subsubitem, each item except for the last must be a package; the last item can be a module or a package but can’t be a class or function or variable defined in the previous item.

于是那就奇怪了,这个os既然不是package,怎么能 import os.path 呢?

本着技术事问咕果的原则,搜到这么一篇,其中提到,python在初始化时会加载一堆模块,其中就包括os,而os模块判断系统类型之后,选定了posixpath模块来配合工作,让它在本命名空间里叫path,还又把它以os.path的名字加入了sys.modules之中;待我执行import os.path时,import语句发现sys.modules里已经有了,于是直接做个symbol过来就了事。

本来事情到这里已经结束了。但是水木上有朋友在参与讨论的时候,为了说明上述其实是语言引擎的行为,而不是os的特殊性导致的,例子如下:

a.py文件:

import sys

import json as shit

sys.modules[‘a.shit’] = shit

然后从另一处import a.shit 居然也成功!

如果从a.py里去掉第三行,虽然import a.shit时会报告NameError: name ‘a’ is not defined,但sys.modules里已经出现了a,这说明a模块已经被import进来了!

 

如果说os.path含着金钥匙是因为它爸叫os,那a.shit又算老几,凭什么和os.path享受一样的待遇?

经过我精心构造的实验,不用a.py文件,而用一个空目录a代替,在python里import a.shit时,用strace偷偷的录下它的可耻行径,发现python居然先尝试找名为a的package(打开a目录找__init__.py)未果之后又尝试找各种形式文件名的名为a的module。

最后的结论是:虽然os.path它爸叫os,但其实即使它爸不叫os,也照样能成功import进来,根源在于import命令并不是像文档所说的,对 import A.B.C这种形式,坚持了A.B必须为package的原则,而是宽进(语言行为)严出(文档叙述),言行不一。

这样不好。

This entry was posted in 默认分类 and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *