模塊與包

遞歸

遞歸調用:在調用一個函數的過程當中,直接或間接地調用了函數自己
直接
def func():
    print('from func')
    func()

func()

間接
def foo():
    print('from foo')
    bar()

def bar():
    print('from bar')
    foo()

foo()
遞歸的執行分爲兩個階段:
1 遞推
2 回溯

遞歸特性:html

1. 必須有一個明確的結束條件python

2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減小linux

3. 遞歸效率不高,遞歸層次過多會致使棧溢出(在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出)git

堆棧掃盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html 

尾遞歸優化:http://egon09.blog.51cto.com/9161406/1842475

二分查找

data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
def binary_search(dataset,find_num):
    print(dataset)
 
    if len(dataset) >1:
        mid = int(len(dataset)/2)
        if dataset[mid] == find_num:  #find it
            print("找到數字",dataset[mid])
        elif dataset[mid] > find_num :# 找的數在mid左面
            print("\033[31;1m找的數在mid[%s]左面\033[0m" % dataset[mid])
            return binary_search(dataset[0:mid], find_num)
        else:# 找的數在mid右面
            print("\033[32;1m找的數在mid[%s]右面\033[0m" % dataset[mid])
            return binary_search(dataset[mid+1:],find_num)
    else:
        if dataset[0] == find_num:  #find it
            print("找到數字啦",dataset[0])
        else:
            print("沒的分了,要找的數字[%s]不在列表裏" % find_num)
 
 
binary_search(data,66)

 協程函數

協程函數就是使用了yield表達式形式的生成器程序員

def eater(name):
    print("%s eat food" %name)
    while True:
        food = yield
    print("done")

g = eater("gangdan")
print(g)

結果:
<generator object eater at 0x0000026AFE4968E0>
這裏就證實了g如今就是生成器函數shell

1. 2 協程函數賦值過程

用的是yield的表達式形式編程

要先運行next(),讓函數初始化並停在yield,至關於初始化函數,而後再send() ,send會給yield傳一個值
** next()和send() 都是讓函數在上次暫停的位置繼續運行,api

next是讓函數初始化緩存

send在觸發下一次代碼的執行時,會給yield賦值bash

def eater(name):
    print('%s start to eat food' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s get %s ,to start eat' %(name,food))
        food_list.append(food)


e=eater('鋼蛋')  # wrapper('')
# print(e)
print(next(e))     # 如今是運行函數,讓函數初始化
print(e.send('包子'))  #
print(e.send('韭菜餡包子'))
print(e.send('大蒜包子'))

這裏的關鍵是:
要先運行next()函數

用裝飾器函數把next()函數先運行一次:

import os
def init(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)
        return res
    return wrapper
@init
def search(target):
    while True:
        search_path=yield
        g=os.walk(search_path)
        for par_dir,_,files in files:
            for file in files:
                file_abs_path=r'%s\%s' %(par_dir,file)
                #print(file_abs_path)
                target.send(file_abs_path)

1.2 協程函數的應用

過濾一個文件下的子文件、字文件夾的內容中的相應的內容,在Linux中的命令就是 grep -rl 'error /dir

#grep -rl 'error' /dir/
import os
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

#第一階段:找到全部文件的絕對路徑
@init
def search(target):
    while True:
        filepath=yield
        g=os.walk(filepath)
        for pardir,_,files in g:
            for file in files:
                abspath=r'%s\%s' %(pardir,file)
                target.send(abspath)
# search(r'C:\Users\Administrator\PycharmProjects\python18期週末班\day5\aaa')
# g=search()
# g.send(r'C:\Python27')

#第二階段:打開文件
@init
def opener(target):
    while True:
        abspath=yield
        with open(abspath,'rb') as f:
            target.send((abspath,f))




#第三階段:循環讀出每一行內容
@init
def cat(target):
    while True:
        abspath,f=yield #(abspath,f)
        for line in f:
            res=target.send((abspath,line))
            if res:break



#第四階段:過濾
@init
def grep(pattern,target):
    tag=False
    while True:
        abspath,line=yield tag
        tag=False
        if pattern in line:
            target.send(abspath)
            tag=True


#第五階段:打印該行屬於的文件名
@init
def printer():
    while True:
        abspath=yield
        print(abspath)

g = search(opener(cat(grep('os'.encode('utf-8'), printer()))))
# g.send(r'C:\Users\Administrator\PycharmProjects\python18期週末班\day5\aaa')

g.send(r'C:\Users\Administrator\PycharmProjects\python18期週末班')
#a1.txt,a2.txt,b1.txt

面向過程的編程思想:流水線式的編程思想,在設計程序時,須要把整個流程設計出來

優勢:
1:體系結構更加清晰
2:簡化程序的複雜度

缺點:
1:可擴展性極其的差,因此說面向過程的應用場景是:不須要常常變化的軟件,如:linux內核,httpd,git等軟件

模塊

1,什麼是模塊(python裏叫模塊,其它叫類庫)?

一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴

  1 使用python編寫的代碼(.py文件)

  2 已被編譯爲共享庫或DLL的C或C++擴展

  3 包好一組模塊的包

  4 使用C編寫並連接到python解釋器的內置模塊

相似於函數式編程和麪向過程編程,函數式編程則完成一個功能,其餘代碼用來調用便可,提供了代碼的重用性和代碼間的耦合。而對於一個複雜的功能來,可能須要多個函數才能完成(函數又能夠在不一樣的.py文件中),n個 .py 文件組成的代碼集合就稱爲模塊。

2,爲什麼要使用模塊?

若是你退出python解釋器而後從新進入,那麼你以前定義的函數或者變量都將丟失,所以咱們一般將程序寫到文件中以便永久保存下來,須要時就經過python test.py方式去執行,此時test.py被稱爲腳本script。

隨着程序的發展,功能愈來愈多,爲了方便管理,咱們一般將程序分紅一個個的文件,這樣作程序的結構更清晰,方便管理,這時咱們不只僅能夠把這些文件看成腳本去執行,還能夠把他們看成模塊來導入到其餘的模塊中,實現了功能的重複利用。

 示例文件:spam.py,文件名spam.py,模塊名spam

 1 #spam.py
 2 print('from the spam.py')
 3 
 4 money=1000
 5 
 6 def read1():
 7     print('spam->read1->money',money)
 8 
 9 def read2():
10     print('spam->read2 calling read')
11     read1()
12 
13 def change():
14     global money
15     money=0

模塊分三種:

自定義模塊

內置模塊

第三方模塊

     安裝:pip3

             源碼

自定義模塊

內置模塊是Python自帶的功能,在使用內置模塊相應的功能時,須要【先導入】再【使用】

import

 模塊能夠包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行(import語句是能夠在程序中的任意位置使用的,且針對同一個模塊很import屢次,爲了防止你重複導入,python的優化手段是:第一次導入後就將模塊名加載到內存了,後續的import語句僅是對已經加載大內存中的模塊對象增長了一次引用,不會從新執行模塊內的語句)以下:

 import spam #只在第一次導入時才執行spam.py內代碼,此處的顯式效果是隻打印一次'from the spam.py',固然其餘的頂級代碼也都被執行了,只不過沒有顯示效果.
 import spam
 import spam
 import spam 
 '''
 執行結果:
 from the spam.py

導入模塊

Python之因此應用愈來愈普遍,在必定程度上也依賴於其爲程序員提供了大量的模塊以供使用,若是想要使用模塊,則須要導入(導入模塊其實就是告訴Python解釋器去解釋那個py文件)導入模塊有一下幾種方法:

1,import module
2,from module.xx.xx import xx
3,from module.xx.xx import xx as rename (爲模塊名起別名,至關於rename=r)
3,1 as示範用法
   if file_format == 'xml':    import xmlreader as reader    elif file_format == 'csv':    import csvreader as reader    data=reader.read_date(filename)
4,from module.xx.xx import *

在一行導入多個模塊

 import sys,os,re

咱們能夠從sys.module中找到當前已經加載的模塊,sys.module是一個字典,內部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否須要從新導入。

導入模塊其實就是告訴Python解釋器去解釋那個py文件

  • 導入一個py文件,解釋器解釋該py文件
  • 導入一個包,解釋器解釋該包下的 __init__.py 文件 【py2.7】

那麼問題來了,導入模塊時是根據那個路徑做爲基準來進行的呢?即:sys.path

import sys
print(sys.path)

結果:
['C:\\Users\\Administrator\\PycharmProjects\\python18\\day5', 'C:\\Users\\Administrator\\PycharmProjects\\python18', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\python36.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\DLLs', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\lib', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages']

若是sys.path路徑列表沒有你想要的路徑,能夠經過 sys.path.append('路徑') 添加。

import sys
import os
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(project_path)

導入模塊乾的事:

1,產生新的名稱空間

2,以新建的名稱空間爲全局名稱空間,執行文件的代碼

3,拿到一個模塊名spam,指向spam.py產生的名稱空間

 每一個模塊都是一個獨立的名稱空間,定義在這個模塊中的函數,把這個模塊的名稱空間當作全局名稱空間,這樣咱們在編寫本身的模塊時,就不用擔憂咱們定義在本身模塊中全局變量會在被導入時,與使用者的全局變量衝突

money與spam.money不衝突
 2 #test.py
 3 import spam 
 4 money=10
 5 print(spam.money)
 6 
 7 '''
 8 執行結果:
 9 from the spam.py
10 1000
11 '''

from......import......

對比import spam,會將源文件的名稱空間'spam'帶到當前名稱空間中,使用時必須是spam.名字的方式

而from 語句至關於import,也會建立新的名稱空間,可是將spam中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就能夠了、

須要注意的是from後import導入的模塊,必須是明確的一個不能帶點,不然會有語法錯誤,如:from a import b.c是錯誤語法

from spam import read1,read2

這樣在當前位置直接使用read1和read2就行了,執行時,仍然以spam.py文件全局名稱空間

#測試一:導入的函數read1,執行時仍然回到spam.py中尋找全局變量money
#test.py
from spam import read1
money=1000
read1()
'''
執行結果:
from the spam.py
spam->read1->money 1000
'''

#測試二:導入的函數read2,執行時須要調用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
執行結果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

若是當前有重名read1或者read2,那麼會有覆蓋效果。

 1 from spam import money,read1
 2 money=100 #將當前位置的名字money綁定到了100
 3 print(money) #打印當前的名字
 4 read1() #讀取spam.py中的名字money,仍然爲1000
 5 
 6 '''
 7 from the spam.py
 8 100
 9 spam->read1->money 1000
10 '''

也支持as

 from spam import read1 as read

也支持導入多行

1 from spam import (read1,
2                   read2,
3                   money)

from spam import * 把spam中全部的不是如下劃線(_)開頭的名字都導入到當前位置,大部分狀況下咱們的python程序不該該使用這種導入方式,由於*你不知道你導入什麼名字,頗有可能會覆蓋掉你以前已經定義的名字。並且可讀性極其的差,在交互式環境中導入時沒有問題。

可使用__all__來控制*(用來發布新版本)

在spam.py中新增一行

__all__=['money','read1'] #這樣在另一個文件中用from spam import *就這能導入列表中規定的兩個名字

若是spam.py中的名字前加_,即_money,則from spam import *,則_money不能被導入

 把模塊當作腳本執行 

咱們能夠經過模塊的全局變量__name__來查看模塊名:
當作腳本運行:
__name__ 等於'__main__'(系統的內置變量)

當作模塊導入:
__name__=

做用:用來控制.py文件在不一樣的應用場景下執行不一樣的邏輯
if __name__ == '__main__':

模塊搜索路徑

python解釋器在啓動時會自動加載一些模塊,可使用sys.modules查看

在第一次導入某個模塊時(好比spam),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),若是有則直接引用

若是沒有,解釋器則會查找同名的內建模塊,若是尚未找到就從sys.path給出的目錄列表中依次尋找spam.py文件。

因此總結模塊的查找順序是:內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊

搜索路徑:
當一個命名爲spam的模塊被導入時
    解釋器首先會從內建模塊中尋找該名字
    找不到,則去sys.path中找該名字

sys.path從如下位置初始化
    1 執行文件所在的當前目錄
    2 PTYHONPATH(包含一系列目錄名,與shell變量PATH語法同樣)
    3 依賴安裝時默認指定的

注意:在支持軟鏈接的文件系統中,執行腳本所在的目錄是在軟鏈接以後被計算的,換句話說,包含軟鏈接的目錄不會被添加到模塊的搜索路徑中

在初始化後,咱們也能夠在python程序中修改sys.path,執行文件所在的路徑默認是sys.path的第一個目錄,在全部標準庫路徑的前面。這意味着,當前目錄是優先於標準庫目錄的,須要強調的是:咱們自定義的模塊名不要跟python標準庫的模塊名重複,除非你是故意的,傻叉。

 編譯python模塊

爲了提升加載模塊的速度,強調強調強調:提升的是加載速度而絕非運行速度。python解釋器會在__pycache__目錄中下緩存每一個模塊編譯後的版本,格式爲:module.version.pyc。一般會包含python的版本號。例如,在CPython3.3版本下,spam.py模塊會被緩存成__pycache__/spam.cpython-33.pyc。這種命名規範保證了編譯後的結果多版本共存。

 

Python檢查源文件的修改時間與編譯的版本進行對比,若是過時就須要從新編譯。這是徹底自動的過程。而且編譯的模塊是平臺獨立的,因此相同的庫能夠在不一樣的架構的系統之間共享,即pyc使一種跨平臺的字節碼,相似於JAVA火.NET,是由python虛擬機來執行的,可是pyc的內容跟python的版本相關,不一樣的版本編譯後的pyc文件不一樣,2.5編譯的pyc文件不能到3.5上執行,而且pyc文件是能夠反編譯的,於是它的出現僅僅是用來提高模塊的加載速度的。

 

python解釋器在如下兩種狀況下不檢測緩存
  1 若是是在命令行中被直接導入模塊,則按照這種方式,每次導入都會從新編譯,而且不會存儲編譯後的結果(python3.3之前的版本應該是這樣)

  2 若是源文件不存在,那麼緩存的結果也不會被使用,若是想在沒有源文件的狀況下來使用編譯後的結果,則編譯後的結果必須在源目錄下

提示:

1.模塊名區分大小寫,foo.py與FOO.py表明的是兩個模塊

2.你可使用-O或者-OO轉換python命令來減小編譯模塊的大小

1 -O轉換會幫你去掉assert語句
2 -OO轉換會幫你去掉assert語句和__doc__文檔字符串
3 因爲一些程序可能依賴於assert語句或文檔字符串,你應該在在確認須要的狀況下使用這些選項。

3.在速度上從.pyc文件中讀指令來執行不會比從.py文件中讀指令執行更快,只有在模塊被加載時,.pyc文件纔是更快的

4.只有使用import語句是纔將文件自動編譯爲.pyc文件,在命令行或標準輸入中指定運行腳本則不會生成這類文件,於是咱們可使用compieall模塊爲一個目錄中的全部模塊建立.pyc文件

1 模塊能夠做爲一個腳本(使用python -m compileall)編譯Python源
2  
3 python -m compileall /module_directory 遞歸着編譯
4 若是使用python -O -m compileall /module_directory -l則只一層
5  
6 命令行裏使用compile()函數時,自動使用python -O -m compileall
7  
8 詳見:https://docs.python.org/3/library/compileall.html#module-compileall

dir函數

內建函數dir是用來查找模塊中定義的名字,返回一個有序字符串列表
import spam
dir(spam)

若是沒有參數,dir()列舉出當前定義的名字


dir()不會列舉出內建函數或者變量的名字,它們都被定義到了標準模塊builtin中,能夠列舉出它們,
import builtins
dir(builtins)

一,sys

用於提供對Python解釋器相關的操做:

sys.argv           命令行參數List,第一個元素是程序自己路徑
sys.exit(n)        退出程序,正常退出時exit(0)
sys.version        獲取Python解釋程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模塊的搜索路徑,初始化時使用PYTHONPATH環境變量的值
sys.platform       返回操做系統平臺名稱
sys.stdin          輸入相關
sys.stdout         輸出相關
sys.stderror       錯誤相關

2、os   用於提供系統級別的操做(提供對操做系統進行調用的藉口)

os.getcwd()                       獲取當前工做目錄,即當前python腳本工做的目錄路徑
os.chdir("dirname")               改變當前腳本工做目錄;至關於shell下cd
os.curdir                         返回當前目錄: ('.')
os.pardir                         獲取當前目錄的父目錄字符串名:('..')
os.makedirs('dir1/dir2')          可生成多層遞歸目錄
os.removedirs('dirname1')         若目錄爲空,則刪除,並遞歸到上一級目錄,如若也爲空,則刪除,依此類推
os.mkdir('dirname')               生成單級目錄;至關於shell中mkdir dirname
os.rmdir('dirname')               刪除單級空目錄,若目錄不爲空則沒法刪除,報錯;至關於shell中rmdir dirname
os.listdir('dirname')             列出指定目錄下的全部文件和子目錄,包括隱藏文件,並以列表方式打印
os.remove()                       刪除一個文件
os.rename("oldname","new")        重命名文件/目錄
os.stat('path/filename')          獲取文件/目錄信息
os.sep                            操做系統特定的路徑分隔符,win下爲"\\",Linux下爲"/"
os.linesep                        當前平臺使用的行終止符,win下爲"\t\n",Linux下爲"\n"
os.pathsep                        用於分割文件路徑的字符串
os.name                           字符串指示當前使用平臺。win->'nt'; Linux->'posix'
os.system("bash command")         運行shell命令,直接顯示
os.environ                        獲取系統環境變量
os.path.abspath(path)             返回path規範化的絕對路徑
os.path.split(path)               將path分割成目錄和文件名二元組返回
os.path.dirname(path)             返回path的目錄。其實就是os.path.split(path)的第一個元素
os.path.basename(path)            返回path最後的文件名。如何path以/或\結尾,那麼就會返回空值。即os.path.split(path)的第二個元素
os.path.exists(path)              若是path存在,返回True;若是path不存在,返回False
os.path.isabs(path)               若是path是絕對路徑,返回True
os.path.isfile(path)              若是path是一個存在的文件,返回True。不然返回False
os.path.isdir(path)               若是path是一個存在的目錄,則返回True。不然返回False
os.path.join(path1[, path2[, ...]])  將多個路徑組合後返回,第一個絕對路徑以前的參數將被忽略
os.path.getatime(path)            返回path所指向的文件或者目錄的最後存取時間
os.path.getmtime(path)            返回path所指向的文件或者目錄的最後修改時間
二 包(包是一種經過使用模塊名來組織python模塊名稱空間的方式)
1,不管是import形式仍是from.......import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提升警覺:這是關於包纔有的導入語法

2,包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質就是一個包含__init__.py文件的目錄)

3,import導入文件時,產生名稱空間中的名字來源於文件,import包,產生的名稱空間的名字一樣來源於文件,即包下的__init__.py,導入包本質就是在導入該文件

強調:

  1. 在python3中,即便包下沒有__init__.py文件,import 包仍然不會報錯,而在python2中,包下必定要有該文件,不然import 包報錯

  2. 建立包的目的不是爲了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包即模塊

注意事項
1.關於包相關的導入語句也分爲import和from ... import ...兩種,可是不管哪一種,不管在什麼位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,不然非法。能夠帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。

2.對於導入後,在使用時就沒有這種限制了,點的左邊能夠是包,模塊,函數,類(它們均可以用點的方式調用本身的屬性)。

3.對比import item 和from item import name的應用場景:
若是咱們想直接使用name那必須使用後者。

絕對導入和相對導入

咱們的最頂級包glance是寫給別人用的,而後在glance包內部也會有彼此之間互相導入的需求,這時候就有絕對導入和相對導入兩種方式:

絕對導入:以glance做爲起始

相對導入:用.或者..的方式最爲起始(只能在一個包中使用,不能用於不一樣目錄內)

1 在glance/api/version.py
2 
3 #絕對導入
4 from glance.cmd import manage
5 manage.main()
6 
7 #相對導入
8 from ..cmd import manage
9 manage.main()

測試結果:注意必定要在於glance同級的文件中測試

from glance.api import versions 

注意:在使用pycharm時,有的狀況會爲你多作一些事情,這是軟件相關的東西,會影響你對模塊導入的理解,於是在測試時,必定要回到命令行去執行,模擬咱們生產環境,你總不能拿着pycharm去上線代碼吧!!!

特別須要注意的是:能夠用import導入內置或者第三方模塊(已經在sys.path中),可是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。

軟件開發規範 

logging日誌(日誌處理之logging模塊)

日誌級別:

debug     最詳細的調試信息  10

info         就想記錄一下    20

warning   出現警告     30

error        出現錯誤    40

crttcal       很是嚴重    50

從大至小,級別越高,數字越大!

日誌格式

%(name)s

Logger的名字

%(levelno)s

數字形式的日誌級別

%(levelname)s

文本形式的日誌級別

%(pathname)s

誰調用日誌輸出該函數的模塊的完整路徑名,可能沒有

%(filename)s

誰調用日誌輸出該函數的模塊的文件名(aa.py)

%(module)s

誰調用日誌輸出該函數的模塊名(aa)

%(funcName)s

誰調用日誌輸出該函數的函數名

%(lineno)d

誰調用日誌輸出該函數的語句所在的代碼行

%(created)f

當前時間,用UNIX標準的表示時間的浮 點數表示

%(relativeCreated)d

輸出日誌信息時的,自Logger建立以 來的毫秒數

%(asctime)s

字符串形式的當前時間。默認格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒

%(thread)d

線程ID。可能沒有

%(threadName)s

線程名。可能沒有

%(process)d

進程ID。可能沒有

%(message)s

用戶輸出的消息

Python 使用logging模塊記錄日誌涉及四個主要類,使用官方文檔中的歸納最爲合適:

logger提供了應用程序能夠直接使用的接口;

handler將(logger建立的)日誌記錄發送到合適的目的輸出(輸出到文件內或屏幕)

filter提供了細度設備來決定輸出哪條日誌記錄(日誌內容哪些輸出,哪些不輸出)

formatter決定日誌記錄的最終輸出格式(日誌格式)

logger
  每一個程序在輸出信息以前都要得到一個Logger。Logger一般對應了程序的模塊名,好比聊天工具的圖形界面模塊能夠這樣得到它的Logger:
  LOG=logging.getLogger(」chat.gui」)
而核心模塊能夠這樣:
  LOG=logging.getLogger(」chat.kernel」)

Logger.setLevel(lel):指定最低的日誌級別,低於lel的級別將被忽略。debug是最低的內置級別,critical爲最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增長或刪除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():能夠設置的日誌級別

 handler

handler對象負責發送相關的信息到指定目的地。Python的日誌系統有多種Handler可使用。有些Handler能夠把信息輸出到控制檯,有些Logger能夠把信息輸出到文件,還有些 Handler能夠把信息發送到網絡上。若是以爲不夠用,還能夠編寫本身的Handler。能夠經過addHandler()方法添加多個多handler
Handler.setLevel(lel):指定被處理的信息級別,低於lel級別的信息將被忽略
Handler.setFormatter():給這個handler選擇一個格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象


每一個Logger能夠附加多個Handler。接下來咱們就來介紹一些經常使用的Handler:
1) logging.StreamHandler
使用這個Handler能夠向相似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息。它的構造函數是:
StreamHandler([strm])
其中strm參數是一個文件對象。默認是sys.stderr


2) logging.FileHandler
和StreamHandler相似,用於向一個文件輸出日誌信息。不過FileHandler會幫你打開這個文件。它的構造函數是:
FileHandler(filename[,mode])
filename是文件名,必須指定一個文件名。
mode是文件的打開方式。參見Python內置函數open()的用法。默認是’a',即添加到文件末尾。

3) logging.handlers.RotatingFileHandler
這個Handler相似於上面的FileHandler,可是它能夠管理文件大小。當文件達到必定大小以後,它會自動將當前日誌文件更名,而後建立 一個新的同名日誌文件繼續輸出。好比日誌文件是chat.log。當chat.log達到指定的大小以後,RotatingFileHandler自動把 文件更名爲chat.log.1。不過,若是chat.log.1已經存在,會先把chat.log.1重命名爲chat.log.2。。。最後從新建立 chat.log,繼續輸出日誌信息。它的構造函數是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個參數和FileHandler同樣。
maxBytes用於指定日誌文件的最大文件大小。若是maxBytes爲0,意味着日誌文件能夠無限大,這時上面描述的重命名過程就不會發生。
backupCount用於指定保留的備份文件的個數。好比,若是指定爲2,當上面描述的重命名過程發生時,原有的chat.log.2並不會被改名,而是被刪除。


4) logging.handlers.TimedRotatingFileHandler
這個Handler和RotatingFileHandler相似,不過,它沒有經過判斷文件大小來決定什麼時候從新建立日誌文件,而是間隔必定時間就 自動建立新的日誌文件。重命名的過程與RotatingFileHandler相似,不過新的文件不是附加數字,而是當前時間。它的構造函數是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數和backupCount參數和RotatingFileHandler具備相同的意義。
interval是時間間隔。

when參數是一個字符串。表示時間間隔的單位,不區分大小寫。它有如下取值:
S 秒
M 分
H 小時
D 天
W 每星期(interval==0時表明星期一)
midnight 天天凌晨

import logging
logging.basicConfig(filename='example.log',
       level=logging.WARNING,
       format='%(asctime)s %(message)s',
       datefmt='%m/%d/%Y %H:%M:%S %p')
#filename='example.log' 指定寫入日誌文件
#level=logging.WARNING(日誌級別必須大寫),日誌級別達到WARNING級別寫入db文件(文件默認生成)
#format='%(asctime)s %(message)s'日誌格式, asctime當前時間, message當前內容
#datefmt='%m/%d/%Y %H:%M:%S %p'  日期格式logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.error("wrong password more than 3 times")
logging.critical("server is down")
#db文件顯示
31/07/2017 14:01:54 AM And this, too06/07/2017 01:01:54 AM wrong password more than 3 times

31/07/2017 14:01:54 AM server is down 

日誌模式輸入到全局日誌,db日誌文件,終端

import logging#日誌輸入到全局日誌,db日誌文件 終端
logger = logging.getLogger('TEST-LOG')      #TEST-LOG是日誌名
logger.setLevel(logging.DEBUG)              #logging.DEBUG 全局的日誌級別
# 全局日誌基本最高,子logging基本必須比全局高 不然不顯示日誌輸出
ch = logging.StreamHandler()                      #建立屏幕輸出顯示
ch.setLevel(logging.INFO)                         #屏幕輸出日誌級別
fh = logging.FileHandler("access.log")         #文件的hander
fh.setLevel(logging.WARNING)                   #文件的日誌級別

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)          #定義日誌格式
fh.setFormatter(formatter)          #定義日誌格式
logger.addHandler(ch)               #屏幕輸出綁定到logger接口
logger.addHandler(fh)               #文件輸出綁定到logger接口#應用程序代碼
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

日誌輸入到全局日誌,db日誌文件 屏幕 並按照時間切割保留最近3個db文件,每5秒切割1次

import logging
from logging import handlers   #建立db文件日誌切割,不加切割不用寫
#日誌輸入到全局日誌,db日誌文件 屏幕 並安裝時間日誌切割保留最近3個文件,每5秒切割1次
logger = logging.getLogger('TEST-LOG')      #TEST-LOG是日誌名
logger.setLevel(logging.DEBUG)              #logging.DEBUG 全局的日誌級別

# 全局日誌基本最高,子logging基本必須比全局高 不然不顯示日誌輸出 ch = logging.StreamHandler() #建立屏幕輸出顯示 ch.setLevel(logging.INFO) #屏幕輸出日誌級別 #fh = logging.FileHandler("access.log") #文件顯示(db文件保存日誌) 不加日誌切割用這個 fh = handlers.TimedRotatingFileHandler("access.log",when="S",interval=5,backupCount=3) #文件顯示(db文件保存日誌) #TimedRotatingFileHandler 按時間切割保存, when="S",單位是秒(h是小時), interval=5,(5個單位切1次)backupCount=3 (保存最近3次,0是無限制保存) #fh = handlers.RotatingFileHandler("access.log",maxBytes=4,backupCount=3) #RotatingFileHandler 按時間切割保存 maxBytes=4 (文件滿4bytes,就生成新文件) backupCount=3 (保存最近3次,0是無限制保存) fh.setLevel(logging.WARNING) #文件的日誌級別 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) #定義日誌格式 fh.setFormatter(formatter) #定義日誌格式 logger.addHandler(ch) #屏幕輸出綁定到logger接口 logger.addHandler(fh) #文件輸出綁定到logger接口 # 應用程序代碼 logger.debug('debug message') logger.info('info message') logger.warning('warn message') logger.error('error message') logger.critical('critical message')
相關文章
相關標籤/搜索