替換print?print怎麼了?html
print 多是全部學習Python語言的人第一個接觸的東西。它最主要的功能就是往控制檯 打印一段信息,像這樣:python
print 'Hello, logging!'
print也是絕大多數人用來調試本身的程序用的最多的東西,就像寫js使用 console.log 同樣那麼天然。不少剛剛開始學習Python的新手甚至有必定經驗的老手,都在使用print 來調試他們的代碼。小程序
好比這是一個我寫的輸出 斐波那契數列 的小程序,讓咱們來看看它的代碼:學習
# -*- coding: utf-8 -*-""" A simple fibonacci program """import argparse parser = argparse.ArgumentParser(description='I print fibonacci sequence') parser.add_argument('-s', '--start', type=int, dest='start', help='Start of the sequence', required=True) parser.add_argument('-e', '--end', type=int, dest='end', help='End of the sequence', required=True)def infinite_fib(): a, b = 0, 1 yield a yield b while True: #print 'Before caculation: a, b = %s, %s' % (a, b) a, b = b, a + b #print 'After caculation: a, b = %s, %s' % (a, b) yield bdef fib(start, end): for cur in infinite_fib(): #print 'cur: %s, start: %s, end: %s' % (cur, start, end) if cur > end: return if cur >= start: #print 'Returning result %s' % cur yield curdef main(): args = parser.parse_args() for n in fib(args.start, args.end): print n,if __name__ == '__main__': main()
讓咱們來看看它工做的怎麼樣:ui
$ python fib.py -s 1 -e 1001 1 2 3 5 8 13 21 34 55 89 $ python fib.py -s 100 -e 1000 144 233 377 610 987
沒有任何問題,程序正確的完成了它的功能。但等等, 程序裏面的那一堆被註釋掉的print語句是怎麼回事?spa
原來,這是我編寫這個小程序的過程當中,用來 調試(DEBUG) 的輸出信息,在我完成了這 個程序之後,我天然就把這些print給註釋掉了。讓咱們來看看若是把這個print語句打開後結果會怎麼樣?debug
$ python fib.py -s 1 -e 100 cur: 0, start: 1, end: 100 cur: 1, start: 1, end: 100 Returning result 1 1 Before caculation: a, b = 0, 1 After caculation: a, b = 1, 1 cur: 1, start: 1, end: 100 ... ... ... ... (不可勝數的輸出信息)
如你所見,全部的計算過程都被打印出來了。調試
寫的時候加上print,提交代碼的時候還得記得把print語句刪掉/註釋掉,爲何咱們要忍受這樣的麻煩事呢? 讓咱們來介紹咱們的主角 logging ,它幾乎就是爲這種使用情景而生的。日誌
logging模塊是Python內置的日誌模塊,使用它能夠很是輕鬆的處理和管理日誌輸出。 logging模塊最簡單的用法,是直接使用basicConfig方法來對logging進行配置:code
import logging# 設置默認的level爲DEBUG# 設置log的格式logging.basicConfig( level=logging.DEBUG, format="[%(asctime)s] %(name)s:%(levelname)s: %(message)s")# 記錄loglogging.debug(...) logging.info(...) logging.warn(...) logging.error(...) logging.critical(...)
這樣配置完logging之後,而後使用``logging.debug``來替換全部的print語句就能夠了。 咱們會看到這樣的輸出:
[2014-03-18 15:17:45,216] root:cur: 0, start: 1, end: 100 [2014-03-18 15:17:45,216] root:DEBUG: cur: 1, start: 1, end: 100 [2014-03-18 15:17:45,216] root:DEBUG: Returning result 1 [2014-03-18 15:17:45,216] root:DEBUG: Before caculation: a, b = 0, 1 ... ...
上面說的basicConfig方法能夠知足你在絕大多數場景下的使用需求,可是basicConfig有一個 很大的缺點。
調用basicConfig實際上是給root logger添加了一個handler,這樣當你的程序和別的使用了 logging的第三方模塊一塊兒工做時,會影響第三方模塊的logger行爲。這是由logger的繼承特性決定的。
因此咱們須要使用真正的logger:
import logging # 使用一個名字爲fib的logger logger = logging.getLogger('fib') # 設置logger的level爲DEBUG logger.setLevel(logging.DEBUG) # 建立一個輸出日誌到控制檯的StreamHandler hdr = logging.StreamHandler() formatter = logging.Formatter('[%(asctime)s] %(name)s:%(levelname)s: %(message)s') hdr.setFormatter(formatter) # 給logger添加上handler logger.addHandler(hdr)
這樣再使用logger來進行日誌輸出就好了。不過這樣的壞處就是代碼量比basicConfig要大很多。 因此我建議若是是很是簡單的小腳本的話,直接使用basicConfig就能夠,若是是稍微大一些 項目,建議認真配置好logger。
使用了logging模塊之後,經過修改logger的log level,咱們就能夠方便的控制程序的輸出了。 好比咱們能夠爲咱們的斐波那契數列添加一個 -v 參數,來控制打印全部的調試信息。
# 添加接收一個verbose參數 parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help='Enable debug info') # 判斷verboseif args.verbose: logger.setLevel(logging.DEBUG)else: logger.setLevel(logging.ERROR)
這樣,默認狀況下,咱們的小程序是不會打印調試信息的,只有當傳入`-v/--verbose`的時候, 咱們纔會打印出額外的debug信息,就像這樣:
$ python fib.py -s 1 -e 1001 1 2 3 5 8 13 21 34 55 89 $ python fib.py -s 1 -e 100 -v [2014-03-18 15:17:45,216] fib:DEBUG: cur: 0, start: 1, end: 100 [2014-03-18 15:17:45,216] fib:DEBUG: cur: 1, start: 1, end: 100 [2014-03-18 15:17:45,216] fib:DEBUG: Returning result 1 [2014-03-18 15:17:45,216] fib:DEBUG: Before caculation: a, b = 0, 1 ... ...
如你所見,使用了logging之後,何時須要打印DEBUG信息,何時須要關閉, 一切變的無比簡單。
因此,趕忙用logging替換掉你的腳本里的print吧!
以上這些只是介紹了logging模塊最簡單的一些功能,做爲print的替代品來使用,logging 模塊還有不少很是強大好用的功能,好比從文件讀取配置、各類各樣的Handlers等等。 建議閱讀一下logging的官方文檔:
最後附上使用logging模塊的斐波那契數列程序完整代碼:
# -*- coding: utf-8 -*- """ A simple fibonacci program """ import argparse parser = argparse.ArgumentParser(description='I print fibonacci sequence') parser.add_argument('-s', '--start', type=int, dest='start', help='Start of the sequence', required=True) parser.add_argument('-e', '--end', type=int, dest='end', help='End of the sequence', required=True) parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help='Enable debug info') import logging logger = logging.getLogger('fib') logger.setLevel(logging.DEBUG) hdr = logging.StreamHandler() formatter = logging.Formatter( '[%(asctime)s] %(name)s:%(levelname)s: %(message)s') hdr.setFormatter(formatter) logger.addHandler(hdr) def infinite_fib(): a, b = 0, 1 yield a yield b while True: logger.debug('Before caculation: a, b = %s, %s' % (a, b)) a, b = b, a + b logger.debug('After caculation: a, b = %s, %s' % (a, b)) yield b def fib(start, end): for cur in infinite_fib(): logger.debug('cur: %s, start: %s, end: %s' % (cur, start, end)) if cur > end: return if cur >= start: logger.debug('Returning result %s' % cur) yield cur def main(): args = parser.parse_args() if args.verbose: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.ERROR) for n in fib(args.start, args.end): print n, if __name__ == '__main__': main()