遞歸調用:在調用一個函數的過程當中,直接或間接地調用了函數自己
直接 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
用的是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)
過濾一個文件下的子文件、字文件夾的內容中的相應的內容,在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屢次,爲了防止你重複導入,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文件
那麼問題來了,導入模塊時是根據那個路徑做爲基準來進行的呢?即: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 '''
對比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解釋器會在__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是用來查找模塊中定義的名字,返回一個有序字符串列表
import spam
dir(spam)
若是沒有參數,dir()列舉出當前定義的名字
dir()不會列舉出內建函數或者變量的名字,它們都被定義到了標準模塊builtin中,能夠列舉出它們,
import builtins
dir(builtins)
用於提供對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 錯誤相關
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所指向的文件或者目錄的最後修改時間
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的形式。
日誌級別:
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對象負責發送相關的信息到指定目的地。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')