Python有哪些黑魔法?

今天继续分享我的学习小基地,有需要的就加入吧!不管你是Python小白,还是大神,大家只管加入,这里我就不想多说了,加入我送一套以前我学习的资料给大家!

Python的黑魔法当然是各种自省和动态绑定了。

举个例子,Python可以重新绑定解释器的excepthook,这样当程序异常结束时就可以做一些自定义的处理,我自己就一直拿这个配合ipdb进行debug。用以下代码声明一个ExceptionHook:

class ExceptionHook : instance = None def __call__(self, *args, **kwargs) : if self.instance is None: from IPython.core import ultratb self.instance = ultratb.FormattedTB(mode = "Plain", color_scheme = "Linux", call_pdb = 1) return self.instance(*args, **kwargs)

然后

import sys sys.exceptionhook = ExceptionHook()

重设完exceptionhook后,一旦你的代码抛出异常,整个解释器的环境都会被ipdb接管,然后就可以像交互模式下那样使用了。通常我会在里面查一下栈,把必要的对象pickle一下,这样以后复现错误也比较容易。

由于IPython是非GUI的程序,所以即便在SSH里也可以使用这招,完美解决SSH缺少IDE难以debug的窘境。

动态绑定的另一个用处,就是当程序依赖一个修改过的库时,可以把修改的部分剥离出来,在运行时动态绑定到对应的库上去就行。如果修改的是成员方法,需要这样绑定:

from types import MethodType def _foo(self, ...): pass obj.foo = MethodType(_foo, obj)

顺带提一下,pickle也是个非常好用的工具,尽管序列化并不是python的专利。pickle可以用来保存各种运行过程中的对象:

import pickle pickle.dump(xxx, open("xxx.dump", "w")) yyy = pickle.load(open("yyy.dump"))

pickle可以减少很多工作量,尤其是在复现bug时,把正确部分的运行结果pickle下来,这样每次可以从pickle的位置开始运行。跑多个相似的baseline时也有很好的效果。不足的是pickle比较吃硬盘,pickle一堆东西后很容易就十几个G了,而且pickle不能序列化动态生成的对象,比如lambda表达式或者上面提到的动态绑定产生的成员方法。

自省方面,Python可以通过dir()和help()函数分别取得对象下成员的列表和帮助,这个在找不到库文档的时候非常好用。只要开发者在函数下面写了注释,就能在help中看到。

除了上面提到的这些特性,python还有一堆小trick,其他回答里也提到了一些。虽然其中很多是语法糖,不过用好它们可以让程序更pythonic:

类中用__slots__将成员静态化,可以节省大量内存。

装饰器,常见用途如函数计时,亦可用来产生新的函数签名。函数签名会影响传参检查和ide补全,对带不定长参数的函数非常有用。很多库中都会用这种方法来兼容不同版本的API。

生成器,对于只需遍历的数据可以节省大量内存。

*和**参数展开。典型的例子是zip(*list_x)和chain(*list_x),分别相当于转置和concatenate。

if __name__ == "__main__": 检查是否作为主程序调用,用multiprocessing并行时主程序得用这个框起来。

enumerate,例如将一个list变成list2index可以用dict([(x, i) for i, x in enumerate(list_x)])

namedtuple,生成类似于C语言的结构体,同时支持tuple的所有语法。

defaultdict,做统计时不用初始化的dict,可以用lambda实现嵌套构造defaultdict(lambda : defaultdict(int)),甚至递归字典tree = lambda : defaultdict(tree)。

是不是干货满满 = ̄ω ̄=