最近几天有一些工作上的资料统计,需要用到python的一个包,而这个包的logger,是作为全局变量写死的,不能传入参数,也无法在类定义里重载。修改这个包的源代码不是一个好方案,琢磨来琢磨去,总算琢磨明白了,只要在import之后,直接module.xyz.log = my_own_log就可以了。
在琢磨的过程中弄明白了,python的import,原来是这样干的!
################################ ################################ A,import module.xyz: 1)查看sys.modules是否有module: 如果有,什么也不做 如果没有,看看pyc缓存里有没有: 如果有,而且代码文件没变,就从pyc缓存里读取 如果没有,运行module的代码文件,可以是module.py, 也可以是__init__.py(所谓的package),然后运行xyz的代码, 生成所有对象的名称和内存引用的dict表,装入sys.modules,同时, 写入了一份缓存.pyc文件。 2)将module和module.xyz写入本地空间,用dir()可以查看; B,from module.xyz import abc 时,则运行1),没有2),而仅仅把abc装入本地空间。 C,from module.xyz import * 时,则运行1),没有2),把所有对象装入本地空间。 在ABC三种情况下,都运行了1),并不想我之前想的C是最浪费资源的。
所谓的本地空间,每个module都有一个__dict__,保存了所有的对象。dir方法,其实就是__dict__.keys()。locals()或者globals()方法,都是在做类似的工作。python就用最基础的dict和list,解决了看似很复杂的战斗。
import就是只运行一次代码并写入sys.modules以及酌情写入本地空间。只有importlib.reload方法会重新读取代码文件覆盖载入module,否则无论怎么重复import,都不会发生任何事情,但是,这所有的东东,都可以手工读写。
如果有一个包不支持外部配置文件,你在用的时候要覆盖内部配置文件怎么办?
# ############################ # ############################ import nicepackage.org_config import my_config for i in dir(my_config): if i[0] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': nicepackage.org_config.__dict__[i] = my_config.__dict__[i] import nicepackage.nicemodule
随着__init__.py文件的不同,上述代码可能需要修改。这里配置文件内的对象名一律是大写的。
如此简单的注入,但如果不深入的琢磨加上机缘巧合,可能再过若干年都无法想到。
python充满了魔法和陷阱,很多高手写的教程里都没有讲魔法,可能是怕这些魔法对新手而言是陷阱吧。在大型的正规的软件工程里,类似于本文的土办法应该是根本不会出现的。
在写上述内容的时候觉得对于python豁然开朗了。作为跟谭浩强同志学过C语言的业余菜鸟,十多年来写代码都是方法加变量,几千行也是这样,也解开了很久以来对OO的困惑:
从前有一个骑士和一只怪兽相依为命,作战谋生,有一天捡到了一本魔法书,书里有各种神龙/怪兽,还有各种技能/作战/组队方法,附录里有个字典索引,有这些怪兽的召唤语,技能的咒语,可以召唤他们战斗,还能让他们杂交,学新技能,后来,这个骑士成了王——这就是OO programming。