"设计一个完美而又方便的log远远比设计一个逻辑功能要复杂" -- by 我自己

在我看上一个commit的时候,我突然发现,爬虫程序中还有很多地方需要处理异常。一如我最开始的时候说的那样,异常是这种程序中最常见的玩意儿。正当我我在很多函数中准备try的时候,发现,这不和加log一个逻辑方式吗?用装饰器不又可以偷懒吗?在reset到相应的commit之后,使用如下命令:

你应该能看到如下的结果:

没错,这次我把结果和log都输出到文件之中,毕竟用重定向的方法把输出内容打到文件之中只是一个调试时候的懒汉技巧。在真正的运用中,我觉得不可能有人这么做的。你可以打开这几个文件,数字代表页数,而.log文件里面会有log文件内容,他们可能不全,因为我运行的时候有没有按下ctrl+c,我是不记得了。

按照前面不断瞎折腾的做法,在写装饰器函数之前,我还是先把初始化的工作放在了一个独立的函数之中。这个函数也无非是规定了下log的格式,log的输出文件,都是一些常规操作。

而在我的函数中,根据我一直强调的所谓哲学,对于出现的异常,有的是忽略,比如在收集数据的时候突然在一次循环中网络扰动而导致数据拿不到。有的就必须抛出这个异常让程序终止,比如构造BeautifulSoup对象失败。这样的话,一个办法是写两个不同的处理异常的装饰器函数,很明显逻辑差不多,可能差距就是一个rasie一个不raise。作为一个程序员,明明是用一个bool值就能搞定的事情却写两个函数,这很不程序员。那么剩下的问题就是如何在装饰器函数中传入参数了。装饰器函数也是函数,传入参数和写普通函数没有什么区别。

那么接下来就是定义装饰器以及怎么样把这个参数传入装饰器了。整个代码看起来就是这样的:

出去最外层的一个def,其余的部分基本没有差别,依旧很熟悉。你可以看到参数在最里侧的函数(37行)中使用了,决定是raise这个异常还是只是忽略他。这里面多了一个新奇特的东西就是functools.wrap,你会发现去掉这个东西,程序不会收到任何影响。那么这玩意儿到底是干嘛的呢?

前面说了,装饰器是函数调用函数,那么在python就出现了一个问题。在使用装饰器的函数中,当你打印函数的名称,也就是name属性的时候,你会发现名称变成了装饰器函数的名称。有点绕,简单点就是,像下面这样:

如果你打印GetBsObejct.__name__,你会发现输出变成了wrapper。如果你有doc string的话,可能你不知道是什么,不重要,也会被改变。如果你加上了functools.wraps就可以避免这一情况。

说完了这个,再回到装饰器函数本身,在这个装饰器函数中,除了会记录函数的进入和离开的情况,还会把发生异常的的情况打印到log文件中。而有了这个函数,剩下的只需要在你想要加上日志的函数前面加一个@,后面跟着装饰器函数的名称,当然,一个表示是否是抛出这个异常终止程序还是忽略他的参数跟着这个函数传进去。

其实我这里的log函数只能说是一个玩具函数,记录的内容肯定是过于简单和重复。而且没有涉及到log如果写到多少就重新写log,或者使用乒乓机制,让log在多个文件中循环。但是这都并不是很复杂的内容,也许在后面有空闲的时候继续完善这些功能,不过,这都会羁绊于我的懒劲上。

results matching ""

    No results matching ""