您现在所在的位置:首页 >常见问题 > 课程问题 > 奇酷总结:Python学习用得到的黑魔法!

奇酷总结: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:

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

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

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

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

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

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

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

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