day5-模塊、包、時間/日誌/正則模塊

 

第1章 模塊介紹

1.1 什麼是模塊?

#常見的場景:一個模塊就是一個包含了一組功能的python文件,好比spam.py,模塊名爲spam,能夠經過import spam使用。html

#在python中,模塊的使用方式都是同樣的,但其實細說的話,模塊能夠分爲四個通用類別: python

  1 使用python編寫的.py文件mysql

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

  3 把一系列模塊組織到一塊兒的文件夾(注:文件夾下有一個__init__.py文件,該文件夾稱之爲包)程序員

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

1.2 爲什麼要使用模塊?

1.2.1 從文件級別組織程序,更方便管理

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

1.2.2 拿來主義,提高開發效率

一樣的原理,咱們也能夠下載別人寫好的模塊而後導入到本身的項目中使用,這種拿來主義,能夠極大地提高咱們的開發效率sql

#ps:shell

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

 

1.3 以spam.py爲例來介紹模塊的使用:文件名spam.py,模塊名spam

#spam.py

print('from the spam.py')

 

money=1000

 

def read1():

    print('spam模塊:',money)

 

def read2():

    print('spam模塊')

    read1()

 

def change():

    global money

    money=0
spam.py

第2章 使用模塊之import

2.1 如何使用模塊-》import spam

一、第一次導入模塊,會發生3件事,重複導入只會引用以前加載好的結果

1)產生一個新的名稱空間

2)運行spam.py代碼,產生的名字都存放於1的名稱空間中,運行過程當中global關鍵字指向的就是該名稱空間

3)在當前名稱空間拿到一個名字spam,該名字指向1的名稱空間

引用spam.py中名字的方式:spam.名字

強調:被導入的模塊在執行過程當中使用本身獨立的名稱空間做爲全局名稱空間
View Code 

二、起別名:import spam as sm

三、一行導入多個模塊:import time,sys,spam

 

2.2 import的使用

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

 

#test.py

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

import spam

import spam

import spam

 

'''

執行結果:

from the spam.py

'''
import的使用

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

 

2.3 在第一次導入模塊時會作三件事,重複導入會直接引用內存中已經加載好的結果

#1.爲源文件(spam模塊)建立新的名稱空間,在spam中定義的函數和方法如果使用到了global時訪問的就是這個名稱空間。

#2.在新建立的命名空間中執行模塊中包含的代碼,見初始導入import spam

    提示:導入模塊時到底執行了什麼?

    In fact function definitions are also ‘statements’ that are

    ‘executed’; the execution of a module-level function definition

    enters the function name in the module’s global symbol table.

    事實上函數定義也是「被執行」的語句,模塊級別函數定義的執行將函數名放

    入模塊全局名稱空間表,用globals()能夠查看

#3.建立名字spam來引用該命名空間

    這個名字和變量名沒什麼區別,都是‘第一類的’,且使用spam.名字的方式

    能夠訪問spam.py文件中定義的名字,spam.名字與test.py中的名字來自

    兩個徹底不一樣的地方。
View Code 

2.4 被導入模塊有獨立的名稱空間

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

 

測試一:money與spam.money不衝突

#test.py

import spam

money=10

print(spam.money)

 

'''

執行結果:

from the spam.py

1000

'''
測試一:money與spam.money不衝突
測試二:read1與spam.read1不衝突

#test.py

import spam

def read1():

    print('========')

spam.read1()

 

'''

執行結果:

from the spam.py

spam->read1->money 1000

'''
測試二:read1與spam.read1不衝突 
測試三:執行spam.change()操做的全局變量money仍然是spam中的

#test.py

import spam

money=1

spam.change()

print(money)

 

'''

執行結果:

from the spam.py

1

'''
測試三:執行spam.change()操做的全局變量money仍然是spam中的

2.5 爲模塊名起別名

爲已經導入的模塊起別名的方式對編寫可擴展的代碼頗有用

import spam as sm

print(sm.money)

 

有兩中sql模塊mysql和oracle,根據用戶的輸入,選擇不一樣的sql功能

#mysql.py

def sqlparse():

    print('from mysql sqlparse')

#oracle.py

def sqlparse():

    print('from oracle sqlparse')

 

#test.py

db_type=input('>>: ')

if db_type == 'mysql':

    import mysql as db

elif db_type == 'oracle':

    import oracle as db

 

db.sqlparse()
View Code

假設有兩個模塊xmlreader.py和csvreader.py,它們都定義了函數read_data(filename):用來從文件中讀取一些數據,但採用不一樣的輸入格式。能夠編寫代碼來選擇性地挑選讀取模塊。

if file_format == 'xml':

    import xmlreader as reader

elif file_format == 'csv':

    import csvreader as reader

data=reader.read_date(filename)

 

2.6 在一行導入多個模塊

import sys,os,re

第3章 使用模塊之from ... import...

3.1 from...import...的使用

from spam import read1,read2

3.2 from...import 與import的對比

#惟一的區別就是:使用from...import...則是將spam中的名字直接導入到當前的名稱空間中,因此在當前名稱空間中,直接使用名字就能夠了、無需加前綴:spam.

 

#from...import...的方式有好處也有壞處

    好處:使用起來方便了

    壞處:容易與當前執行文件中的名字衝突
View Code

3.2.1 驗證一:

當前位置直接使用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

'''
View Code

3.2.2 驗證二:

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

#測試三:導入的函數read1,被當前位置定義的read1覆蓋掉了

#test.py

from spam import read1

def read1():

    print('==========')

read1()

'''

執行結果:

from the spam.py

==========

'''

 
View Code

3.2.3 驗證三:

導入的方法在執行時,始終是以源文件爲準的

from spam import money,read1

money=100 #將當前位置的名字money綁定到了100

print(money) #打印當前的名字

read1() #讀取spam.py中的名字money,仍然爲1000

 

'''

from the spam.py

100

spam->read1->money 1000

'''

 
View Code

3.3 也支持as

from spam import read1 as read

3.4 一行導入多個名字

from spam import read1,read2,money

3.5 from...import *

#from spam import * 把spam中全部的不是如下劃線(_)開頭的名字都導入到當前位置

#大部分狀況下咱們的python程序不該該使用這種導入方式,由於*你不知道你導入什麼名字,頗有可能會覆蓋掉你以前已經定義的名字。並且可讀性極其的差,在交互式環境中導入時沒有問題。

from spam import * #將模塊spam中全部的名字都導入到當前名稱空間

print(money)

print(read1)

print(read2)

print(change)

 

'''

執行結果:

from the spam.py

1000

<function read1 at 0x1012e8158>

<function read2 at 0x1012e81e0>

<function change at 0x1012e8268>

'''

 
from spam import *

可使用__all__來控制*(用來發布新版本),在spam.py中新增一行

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

第4章 模塊的重載 (瞭解)

考慮到性能的緣由,每一個模塊只被導入一次,放入字典sys.module中,若是你改變了模塊的內容,你必須重啓程序,python不支持從新加載或卸載以前導入的模塊,有的同窗可能會想到直接從sys.module中刪除一個模塊不就能夠卸載了嗎,注意了,你刪了sys.module中的模塊對象仍然可能被其餘程序的組件所引用,於是不會被清楚。

特別的對於咱們引用了這個模塊中的一個類,用這個類產生了不少對象,於是這些對象都有關於這個模塊的引用。

若是隻是你想交互測試的一個模塊,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這隻能用於測試環境。

 

aa.py的初始內容

def func1():

    print('func1')
執行test.py

import time,importlib

import aa

 

time.sleep(20)

# importlib.reload(aa)

aa.func1()
執行test.py

在20秒的等待時間裏,修改aa.py中func1的內容,等待test.py的結果。

打開importlib註釋,從新測試

第5章 py文件區分兩種用途:模塊與腳本

5.1 當作腳本執行:__name__ == '__main__'

5.2 當作模塊被導入使用:__name__ == '模塊名'

if __name__ == '__main__':

pass 

 

#編寫好的一個python文件能夠有兩種用途:

    一:腳本,一個文件就是整個程序,用來被執行

    二:模塊,文件中存放着一堆功能,用來被導入使用

 

 

#python爲咱們內置了全局變量__name__,

    當文件被當作腳本執行時:__name__ 等於'__main__'

    當文件被當作模塊導入時:__name__等於模塊名

 

#做用:用來控制.py文件在不一樣的應用場景下執行不一樣的邏輯

    if __name__ == '__main__':
介紹
#fib.py

 

def fib(n):    # write Fibonacci series up to n

    a, b = 0, 1

    while b < n:

        print(b, end=' ')

        a, b = b, a+b

    print()

 

def fib2(n):   # return Fibonacci series up to n

    result = []

    a, b = 0, 1

    while b < n:

        result.append(b)

        a, b = b, a+b

    return result

 

if __name__ == "__main__":

    import sys

    fib(int(sys.argv[1]))

 

 

#執行:python fib.py <arguments>

python fib.py 50 #在命令行

 
fib.py

第6章 模塊搜索路徑

6.1 簡單說明

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

 

6.2 詳細的說明

#模塊的查找順序

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

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

2、若是沒有,解釋器則會查找同名的內建模塊

3、若是尚未找到就從sys.path給出的目錄列表中依次尋找spam.py文件。

 

 

#sys.path的初始化的值來自於:

The directory containing the input script (or the current directory when no file is specified).

PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).

The installation-dependent default.

 

#須要特別注意的是:咱們自定義的模塊名不該該與系統內置模塊重名。雖然每次都說,可是仍然會有人不停的犯錯。

 

#在初始化後,python程序能夠修改sys.path,路徑放到前面的優先於標準庫被加載。

1 >>> import sys

2 >>> sys.path.append('/a/b/c/d')

3 >>> sys.path.insert(0,'/x/y/z') #排在前的目錄,優先被搜索

注意:搜索時按照sys.path中從左到右的順序查找,位於前的優先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理,

 

#首先製做歸檔文件:zip module.zip foo.py bar.py

import sys

sys.path.append('module.zip')

import foo,bar

 

#也可使用zip中目錄結構的具體位置

sys.path.append('module.zip/lib/python')

 

 

#windows下的路徑不加r開頭,會語法錯誤

sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')

 

 

#至於.egg文件是由setuptools建立的包,這是按照第三方python庫和擴展時使用的一種常見格式,.egg文件實際上只是添加了額外元數據(如版本號,依賴項等)的.zip文件。

 

#須要強調的一點是:只能從.zip文件中導入.py,.pyc等文件。使用C編寫的共享庫和擴展塊沒法直接從.zip文件中加載(此時setuptools等打包系統有時能提供一種規避方法),且從.zip中加載文件不會建立.pyc或者.pyo文件,所以必定要事先建立他們,來避免加載模塊是性能降低。
詳細的介紹

6.3 官網解釋:

#官網連接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path

搜索路徑:

當一個命名爲spam的模塊被導入時

    解釋器首先會從內建模塊中尋找該名字

    找不到,則去sys.path中找該名字

 

sys.path從如下位置初始化

    1 執行文件所在的當前目錄

    2 PTYHONPATH(包含一系列目錄名,與shell變量PATH語法同樣)

    3 依賴安裝時默認指定的

 

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

 

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

 

 
官網解釋

第7章 編譯python文件(瞭解)

7.1 簡單的解釋

爲了提升加載模塊的速度,強調強調強調:提升的是加載速度而絕非運行速度。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文件是能夠反編譯的,於是它的出現僅僅是用來提高模塊的加載速度的,不是用來加密的。

7.2 詳細的解釋

#python解釋器在如下兩種狀況下不檢測緩存

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

    python -m spam.py

 

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

sh-3.2# ls

__pycache__ spam.py

sh-3.2# rm -rf spam.py

sh-3.2# mv __pycache__/spam.cpython-36.pyc ./spam.pyc

sh-3.2# python3 spam.pyc

spam

 

 

#提示:

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

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

    -O轉換會幫你去掉assert語句

    -OO轉換會幫你去掉assert語句和__doc__文檔字符串

    因爲一些程序可能依賴於assert語句或文檔字符串,你應該在在確認須要

    的狀況下使用這些選項。

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

 

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

 

模塊能夠做爲一個腳本(使用python -m compileall)編譯Python源 

python -m compileall /module_directory 遞歸着編譯

若是使用python -O -m compileall /module_directory -l則只一層

 

命令行裏使用compile()函數時,自動使用python -O -m compileall

 

詳見:https://docs.python.org/3/library/compileall.html#module-compileall

 

 
詳細的解釋

 

第8章 包介紹

8.1 什麼是包?

#官網解釋

Packages are a way of structuring Python’s module namespace by using 「dotted module names」

包是一種經過使用‘.模塊名’來組織python模塊名稱空間的方式。

#具體的:包就是一個包含有__init__.py文件的文件夾,因此其實咱們建立包的目的就是爲了用文件夾將文件/模塊組織起來

#須要強調的是:

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

  2. 建立包的目的不是爲了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包的本質就是一種模塊
官網解釋

8.2 爲什麼要使用包

包的本質就是一個文件夾,那麼文件夾惟一的功能就是將文件組織起來

隨着功能越寫越多,咱們沒法將因此功能都放到一個文件中,因而咱們使用模塊去組織功能,而隨着模塊愈來愈多,咱們就須要用文件夾將模塊文件組織起來,以此來提升程序的結構性和可維護性。

 

8.3 注意事項

#1.關於包相關的導入語句也分爲import和from ... import ...兩種,可是不管哪一種,不管在什麼位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,不然非法。能夠帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。但對於導入後,在使用時就沒有這種限制了,點的左邊能夠是包,模塊,函數,類(它們均可以用點的方式調用本身的屬性)。

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

#三、包A和包B下有同名模塊也不會衝突,如A.a與B.a來自倆個命名空間
注意事項

8.4 上課流程

1 實驗一

    準備:

        執行文件爲test.py,內容

        #test.py

        import aaa

        同級目錄下建立目錄aaa,而後自建空__init__.py(或者乾脆建包)

 

    需求:驗證導入包就是在導入包下的__init__.py

 

    解決:

        先執行看結果

        再在__init__.py添加打印信息後,從新執行

 

2、實驗二

    準備:基於上面的結果

 

    需求:

        aaa.x

        aaa.y

    解決:在__init__.py中定義名字x和y

 

3、實驗三

    準備:在aaa下創建m1.py和m2.py

        #m1.py

        def f1():

            print('from 1')

        #m2.py

        def f2():

            print('from 2')

    需求:

        aaa.m1 #進而aaa.m1.func1()

        aaa.m2 #進而aaa.m2.func2()

 

    解決:在__init__.py中定義名字m1和m2,先定義一個普通變量,再引出如何導入模塊名,強調:環境變量是以執行文件爲準

   

 

4、實驗四

    準備:在aaa下新建包bbb

 

    需求:

        aaa.bbb

 

    解決:在aaa的__init__.py內導入名字bbb

 

5、實驗五

    準備:

        在bbb下創建模塊m3.py

        #m3.py

        def f3():

            print('from 3')

    需求:

        aaa.bbb.m3 #進而aaa.bbb.m3.f3()

 

    解決:是bbb下的名字m3,於是要在bbb的__init__.py文件中導入名字m3,from aaa.bbb import m3

 

6、實驗六

    準備:基於上面的結果

 

    需求:

        aaa.m1()

        aaa.m2()

        aaa.m3()

        進而實現

        aaa.f1()

        aaa.f2()

        aaa.f3()

        先用絕對導入,再用相對導入

       

    解決:在aaa的__init__.py中拿到名字m一、m二、m3

    包內模塊直接的相對導入,強調包的本質:包內的模塊是用來被導入的,而不是被執行的

    用戶沒法區分模塊是文件仍是一個包,咱們定義包是爲了方便開發者維護

 

7、實驗七

    將包整理當作一個模塊,移動到別的目錄下,操做sys.path
View Code

第9章 包的使用

9.1 示範文件

glance/                   #Top-level package

 

├── __init__.py      #Initialize the glance package

 

├── api                  #Subpackage for api

 

│   ├── __init__.py

 

│   ├── policy.py

 

│   └── versions.py

 

├── cmd                #Subpackage for cmd

 

│   ├── __init__.py

 

│   └── manage.py

 

└── db                  #Subpackage for db

 

    ├── __init__.py

 

    └── models.py
包的示範文件結構
#包所包含的文件內容

 

#policy.py

def get():

    print('from policy.py')

 

#versions.py

def create_resource(conf):

    print('from version.py: ',conf)

 

#manage.py

def main():

    print('from manage.py')

 

#models.py

def register_models(engine):

    print('from models.py: ',engine)
文件內容

執行文件與示範文件在同級目錄下

 

9.2 包的使用之import

import glance.db.models

glance.db.models.register_models('mysql')

單獨導入包名稱時不會導入包中全部包含的全部子模塊,如

 

#在與glance同級的test.py中

import glance

glance.cmd.manage.main()

 

'''

執行結果:

AttributeError: module 'glance' has no attribute 'cmd'

'''

解決方法:

#glance/__init__.py

from . import cmd

 

#glance/cmd/__init__.py

from . import manage

執行:

#在於glance同級的test.py中

import glance

glance.cmd.manage.main()

9.3 包的使用之from ... import ...

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

from glance.db import models

models.register_models('mysql')

 

from glance.db.models import register_models

register_models('mysql')
View Code

9.4 from glance.api import *

在講模塊時,咱們已經討論過了從一個模塊內導入全部*,此處咱們研究從一個包導入全部*。

此處是想從包api中導入全部,實際上該語句只會導入包api下__init__.py文件中定義的名字,咱們能夠在這個文件中定義__all___:

#在__init__.py中定義

x=10

 

def func():

    print('from api.__init.py')

 

__all__=['x','func','policy']
在__init__.py中定義

此時咱們在於glance同級的文件中執行from glance.api import *就導入__all__中的內容(versions仍然不能導入)。

練習:

#執行文件中的使用效果以下,請處理好包的導入

from glance import *

 

get()

create_resource('a.conf')

main()

register_models('mysql')

 

 

#在glance.__init__.py中

from .api.policy import get

from .api.versions import create_resource

 

from .cmd.manage import main

from .db.models import  register_models

 

__all__=['get','create_resource','main','register_models']
執行文件中的使用效果以下,請處理好包的導入

9.5 絕對導入和相對導入

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

絕對導入:以glance做爲起始

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

例如:咱們在glance/api/version.py中想要導入glance/cmd/manage.py

在glance/api/version.py

 

#絕對導入

from glance.cmd import manage

manage.main()

 

#相對導入

from ..cmd import manage

manage.main()

 

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

from glance.api import versions
View Code

9.6 包以及包所包含的模塊都是用來被導入的,而不是被直接執行的。而環境變量都是以執行文件爲準的

好比咱們想在glance/api/versions.py中導入glance/api/policy.py,有的同窗一抽這倆模塊是在同一個目錄下,十分開心的就去作了,它直接這麼作

#在version.py中

 

import policy

policy.get()

沒錯,咱們單獨運行version.py是一點問題沒有的,運行version.py的路徑搜索就是從當前路徑開始的,因而在導入policy時能在當前目錄下找到

 可是你想啊,你子包中的模塊version.py極有多是被一個glance包同一級別的其餘文件導入,好比咱們在於glance同級下的一個test.py文件中導入version.py,以下

from glance.api import versions

 

 '''

 執行結果:

 ImportError: No module named 'policy'

 '''

 

 '''

 分析:

 此時咱們導入versions在versions.py中執行

 import policy須要找從sys.path也就是從當前目錄找policy.py,

 這必然是找不到的

 '''

 
View Code

9.7 包的分發(瞭解)

https://packaging.python.org/distributing/

 

第10章 軟件開發目錄規範

10.1 實例一

 

 

#===============>star.py

import sys,os

BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

sys.path.append(BASE_DIR)

 

from core import src

 

if __name__ == '__main__':

    src.run()

#===============>settings.py

import os

 

BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DB_PATH=os.path.join(BASE_DIR,'db','db.json')

LOG_PATH=os.path.join(BASE_DIR,'log','access.log')

LOGIN_TIMEOUT=5

 

"""

logging配置

"""

# 定義三種日誌輸出格式

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \

                  '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

 

# log配置字典

LOGGING_DIC = {

    'version': 1,

    'disable_existing_loggers': False,

    'formatters': {

        'standard': {

            'format': standard_format

        },

        'simple': {

            'format': simple_format

        },

    },

    'filters': {},

    'handlers': {

        #打印到終端的日誌

        'console': {

            'level': 'DEBUG',

            'class': 'logging.StreamHandler',  # 打印到屏幕

            'formatter': 'simple'

        },

        #打印到文件的日誌,收集info及以上的日誌

        'default': {

            'level': 'DEBUG',

            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件

            'formatter': 'standard',

            'filename': LOG_PATH,  # 日誌文件

            'maxBytes': 1024*1024*5,  # 日誌大小 5M

            'backupCount': 5,

            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了

        },

    },

    'loggers': {

        #logging.getLogger(__name__)拿到的logger配置

        '': {

            'handlers': ['default', 'console'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕

            'level': 'DEBUG',

            'propagate': True,  # 向上(更高level的logger)傳遞

        },

    },

}

 

 

#===============>src.py

from conf import settings

from lib import common

import time

 

logger=common.get_logger(__name__)

 

current_user={'user':None,'login_time':None,'timeout':int(settings.LOGIN_TIMEOUT)}

def auth(func):

    def wrapper(*args,**kwargs):

        if current_user['user']:

            interval=time.time()-current_user['login_time']

            if interval < current_user['timeout']:

                return func(*args,**kwargs)

        name = input('name>>: ')

        password = input('password>>: ')

        db=common.conn_db()

        if db.get(name):

            if password == db.get(name).get('password'):

                logger.info('登陸成功')

                current_user['user']=name

                current_user['login_time']=time.time()

                return func(*args,**kwargs)

        else:

            logger.error('用戶名不存在')

 

    return wrapper

 

@auth

def buy():

    print('buy...')

 

@auth

def run():

 

    print('''

    1 購物

    2 查看餘額

    3 轉帳

    ''')

    while True:

        choice = input('>>: ').strip()

        if not choice:continue

        if choice == '1':

            buy()

 

 

 

#===============>db.json

{"egon": {"password": "123", "money": 3000}, "alex": {"password": "alex3714", "money": 30000}, "wsb": {"password": "3714", "money": 20000}}

 

#===============>common.py

from conf import settings

import logging

import logging.config

import json

 

def get_logger(name):

    logging.config.dictConfig(settings.LOGGING_DIC)  # 導入上面定義的logging配置

    logger = logging.getLogger(name)  # 生成一個log實例

    return logger

 

 

def conn_db():

    db_path=settings.DB_PATH

    dic=json.load(open(db_path,'r',encoding='utf-8'))

    return dic

 

 

#===============>access.log

[2017-10-21 19:08:20,285][MainThread:10900][task_id:core.src][src.py:19][INFO][登陸成功]

[2017-10-21 19:08:32,206][MainThread:10900][task_id:core.src][src.py:19][INFO][登陸成功]

[2017-10-21 19:08:37,166][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在]

[2017-10-21 19:08:39,535][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在]

[2017-10-21 19:08:40,797][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在]

[2017-10-21 19:08:47,093][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在]

[2017-10-21 19:09:01,997][MainThread:10900][task_id:core.src][src.py:19][INFO][登陸成功]

[2017-10-21 19:09:05,781][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在]

[2017-10-21 19:09:29,878][MainThread:8812][task_id:core.src][src.py:19][INFO][登陸成功]

[2017-10-21 19:09:54,117][MainThread:9884][task_id:core.src][src.py:19][INFO][登陸成功]

 
實例一代碼

10.2 實例二

 

####bin目錄爲ATM執行文件目錄,start.py文件爲ATM執行程序文件,文件內容以下:
import sys,os

BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

from core import src

if __name__ == '__main__':
src.run()


######conf目錄爲ATM配置目錄,settings.py文件爲ATM配置文件,文件內容以下:
import os
import logging.config

BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOG_PATH=os.path.join(BASE_DIR,'log','access.log')
COLLECT_PATH=os.path.join(BASE_DIR,'log','collect.log')
DB_PATH=os.path.join(BASE_DIR,'db','user')


# 定義三種日誌輸出格式 開始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'id_simple' : {
            'format' : id_simple_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到終端的日誌
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日誌,收集info及以上的日誌
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': LOG_PATH,  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了
        },
        'collect': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'simple',
            'filename': COLLECT_PATH,  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了
        },
    },
    'loggers': {
        '': {
            'handlers': ['default', 'console','collect'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': False,  # 向上(更高level的logger)傳遞
        },
    },
}

###########core目錄爲ATM主要核心邏輯程序目錄,src.py文件爲主邏輯程序,文件內容以下:
from lib import common
from lib import sql

def shop():
    print('購物......')

def check_balance():
    print('查看餘額......')
    res=sql.execute('select balance from user where id=3')
    print(res)

def transfer_accounts():
    print('轉帳......')
    #記錄日誌
    log_msg='egon給alex轉了1毛錢'
    # 調用日誌功能記錄日誌
    # common.logger(log_msg)
    logger=common.logger_handle('轉帳')
    logger.debug(log_msg)

def run():
    msg='''
    1 購物
    2 查看餘額
    3 轉帳
    '''
    while True:
        print(msg)
        choice=input('>>: ').strip()
        if not choice:continue
        if choice == '1':
            shop()
        elif choice == '2':
            check_balance()
        elif choice == '3':
            transfer_accounts()

#######db目錄爲用戶數據存儲目錄,user文件爲一個普通用戶帳戶文件,內容以下:
alex,18,male,100000
egon,18,male,100000

#########lib目錄爲其餘人的或本身的模塊和包存放的位置,common.py內容以下:
from conf import settings
import logging.config

# def logger(msg):
#     with open(settings.LOG_PATH,'a',encoding='utf-8') as f:
#         f.write('%s\n' %msg)

def logger_handle(log_name):
    logging.config.dictConfig(settings.LOGGING_DIC)  # 導入上面定義的logging配置
    logger = logging.getLogger(log_name)  # 生成一個log實例
    return logger

#############lib目錄下sql.py內容以下:
def execute(sql):
    print('解析')
    print('執行sql')
#################log目錄爲日誌存放目錄,access.log內容以下:
egon給alex轉了1毛錢
[2018-01-06 16:36:52,892][MainThread:5204][task_id:轉帳][src.py:19][DEBUG][egon給alex轉了1毛錢]

#################log目錄下collect.log內容以下:
[DEBUG][2018-01-06 16:36:52,892][src.py:19]egon給alex轉了1毛錢

################Readme文件爲介紹軟件功能及使用方法,內容以下:(目前爲空)
實例二

第11章 經常使用模塊

11.1 日誌模塊logging

11.1.1 日誌級別

CRITICAL = 50 #FATAL = CRITICAL

ERROR = 40

WARNING = 30 #WARN = WARNING

INFO = 20

DEBUG = 10

NOTSET = 0 #不設置

11.1.2 默認級別爲warning,默認打印到終端

import logging

 

logging.debug('調試debug')

logging.info('消息info')

logging.warning('警告warn')

logging.error('錯誤error')

logging.critical('嚴重critical')

 

'''

WARNING:root:警告warn

ERROR:root:錯誤error

CRITICAL:root:嚴重critical

'''

 
View Code

11.1.3 爲logging模塊指定全局配置,針對全部logger有效,控制打印到文件中

logging.basicConfig()

 

可在logging.basicConfig()函數中經過具體參數來更改logging模塊默認行爲,可用參數有

filename:用指定的文件名建立FiledHandler(後邊會具體講解handler的概念),這樣日誌會被存儲在指定的文件中。

filemode:文件打開方式,在指定了filename時使用這個參數,默認值爲「a」還可指定爲「w」。

format:指定handler使用的日誌顯示格式。

datefmt:指定日期時間格式。

level:設置rootlogger(後邊會講解具體概念)的日誌級別

stream:用指定的stream建立StreamHandler。能夠指定輸出到sys.stderr,sys.stdout或者文件,默認爲sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。

 

 

 

#格式

%(name)s:Logger的名字,並不是用戶名,詳細查看

%(levelno)s:數字形式的日誌級別

%(levelname)s:文本形式的日誌級別

%(pathname)s:調用日誌輸出函數的模塊的完整路徑名,可能沒有

%(filename)s:調用日誌輸出函數的模塊的文件名

%(module)s:調用日誌輸出函數的模塊名

%(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:用戶輸出的消息

 
logging.basicConfig()
#======介紹

可在logging.basicConfig()函數中可經過具體參數來更改logging模塊默認行爲,可用參數有

filename:用指定的文件名建立FiledHandler(後邊會具體講解handler的概念),這樣日誌會被存儲在指定的文件中。

filemode:文件打開方式,在指定了filename時使用這個參數,默認值爲「a」還可指定爲「w」。

format:指定handler使用的日誌顯示格式。

datefmt:指定日期時間格式。

level:設置rootlogger(後邊會講解具體概念)的日誌級別

stream:用指定的stream建立StreamHandler。能夠指定輸出到sys.stderr,sys.stdout或者文件,默認爲sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。

 

 

format參數中可能用到的格式化串:

%(name)s Logger的名字

%(levelno)s 數字形式的日誌級別

%(levelname)s 文本形式的日誌級別

%(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有

%(filename)s 調用日誌輸出函數的模塊的文件名

%(module)s 調用日誌輸出函數的模塊名

%(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用戶輸出的消息

 

 

 

 

#========使用

import logging

logging.basicConfig(filename='access.log',

                    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

                    datefmt='%Y-%m-%d %H:%M:%S %p',

                    level=10)

 

logging.debug('調試debug')

logging.info('消息info')

logging.warning('警告warn')

logging.error('錯誤error')

logging.critical('嚴重critical')

 

 

 

 

 

#========結果

access.log內容:

2017-07-28 20:32:17 PM - root - DEBUG -test:  調試debug

2017-07-28 20:32:17 PM - root - INFO -test:  消息info

2017-07-28 20:32:17 PM - root - WARNING -test:  警告warn

2017-07-28 20:32:17 PM - root - ERROR -test:  錯誤error

2017-07-28 20:32:17 PM - root - CRITICAL -test:  嚴重critical

 

part2: 能夠爲logging模塊指定模塊級的配置,即全部logger的配置
logging模塊介紹

11.1.4 logging模塊的Formatter,Handler,Logger,Filter對象

原理圖:https://pan.baidu.com/s/1skWyTT7

#logger:產生日誌的對象
#Filter:過濾日誌的對象
#Handler:接收日誌而後控制打印到不一樣的地方,FileHandler用來打印到文件中,StreamHandler用來打印到終端
#Formatter對象:能夠定製不一樣的日誌格式對象,而後綁定給不一樣的Handler對象使用,以此來控制不一樣的Handler的日誌格式
'''

critical=50

error =40

warning =30

info = 20

debug =10

'''

 

 

import logging

 

#一、logger對象:負責產生日誌,而後交給Filter過濾,而後交給不一樣的Handler輸出

logger=logging.getLogger(__file__)

 

#二、Filter對象:不經常使用,略

 

#三、Handler對象:接收logger傳來的日誌,而後控制輸出

h1=logging.FileHandler('t1.log') #打印到文件

h2=logging.FileHandler('t2.log') #打印到文件

h3=logging.StreamHandler() #打印到終端

 

#四、Formatter對象:日誌格式

formmater1=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

                    datefmt='%Y-%m-%d %H:%M:%S %p',)

 

formmater2=logging.Formatter('%(asctime)s :  %(message)s',

                    datefmt='%Y-%m-%d %H:%M:%S %p',)

 

formmater3=logging.Formatter('%(name)s %(message)s',)

 

 

#五、爲Handler對象綁定格式

h1.setFormatter(formmater1)

h2.setFormatter(formmater2)

h3.setFormatter(formmater3)

 

#六、將Handler添加給logger並設置日誌級別

logger.addHandler(h1)

logger.addHandler(h2)

logger.addHandler(h3)

logger.setLevel(10)

 

#七、測試

logger.debug('debug')

logger.info('info')

logger.warning('warning')

logger.error('error')

logger.critical('critical')
實例

11.1.5 Logger與Handler的級別

logger是第一級過濾,而後才能到handler,咱們能夠給logger和handler同時設置level,可是須要注意的是

 

###重要,重要,重要!!!

Logger is also the first to filter the message based on a level — if you set the logger to INFO, and all handlers to DEBUG, you still won't receive DEBUG messages on handlers — they'll be rejected by the logger itself. If you set logger to DEBUG, but all handlers to INFO, you won't receive any DEBUG messages either — because while the logger says "ok, process this", the handlers reject it (DEBUG < INFO).

 

 

 

#驗證

import logging

 

 

form=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

                    datefmt='%Y-%m-%d %H:%M:%S %p',)

 

ch=logging.StreamHandler()

 

ch.setFormatter(form)

# ch.setLevel(10)

ch.setLevel(20)

 

l1=logging.getLogger('root')

# l1.setLevel(20)

l1.setLevel(10)

l1.addHandler(ch)

 

l1.debug('l1 debug')
View Code

11.1.6 Logger的繼承(瞭解)

import logging

 

formatter=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

                    datefmt='%Y-%m-%d %H:%M:%S %p',)

 

ch=logging.StreamHandler()

ch.setFormatter(formatter)

 

 

logger1=logging.getLogger('root')

logger2=logging.getLogger('root.child1')

logger3=logging.getLogger('root.child1.child2')

 

 

logger1.addHandler(ch)

logger2.addHandler(ch)

logger3.addHandler(ch)

logger1.setLevel(10)

logger2.setLevel(10)

logger3.setLevel(10)

 

logger1.debug('log1 debug')

logger2.debug('log2 debug')

logger3.debug('log3 debug')

'''

2017-07-28 22:22:05 PM - root - DEBUG -test:  log1 debug

2017-07-28 22:22:05 PM - root.child1 - DEBUG -test:  log2 debug

2017-07-28 22:22:05 PM - root.child1 - DEBUG -test:  log2 debug

2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug

2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug

2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug

'''

 

 
View Code

11.1.7 應用

##### logging配置文件

"""

logging配置

"""

 

import os

import logging.config

 

# 定義三種日誌輸出格式 開始

 

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \

                  '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字

 

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

 

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

 

# 定義日誌輸出格式 結束

 

logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目錄

 

logfile_name = 'all2.log'  # log文件名

 

# 若是不存在定義的日誌目錄就建立一個

if not os.path.isdir(logfile_dir):

    os.mkdir(logfile_dir)

 

# log文件的全路徑

logfile_path = os.path.join(logfile_dir, logfile_name)

 

# log配置字典

LOGGING_DIC = {

    'version': 1,

    'disable_existing_loggers': False,

    'formatters': {

        'standard': {

            'format': standard_format

        },

        'simple': {

            'format': simple_format

        },

    },

    'filters': {},

    'handlers': {

        #打印到終端的日誌

        'console': {

            'level': 'DEBUG',

            'class': 'logging.StreamHandler',  # 打印到屏幕

            'formatter': 'simple'

        },

        #打印到文件的日誌,收集info及以上的日誌

        'default': {

            'level': 'DEBUG',

            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件

            'formatter': 'standard',

            'filename': logfile_path,  # 日誌文件

            'maxBytes': 1024*1024*5,  # 日誌大小 5M

            'backupCount': 5,

            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了

        },

    },

    'loggers': {

        #logging.getLogger(__name__)拿到的logger配置

        '': {

            'handlers': ['default', 'console'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕

            'level': 'DEBUG',

            'propagate': True,  # 向上(更高level的logger)傳遞

        },

    },

}

 

 

def load_my_logging_cfg():

    logging.config.dictConfig(LOGGING_DIC)  # 導入上面定義的logging配置

    logger = logging.getLogger(__name__)  # 生成一個log實例

    logger.info('It works!')  # 記錄該文件的運行狀態

 

if __name__ == '__main__':

    load_my_logging_cfg()

 
logging配置文件
#######使用

"""

MyLogging Test

"""

 

import time

import logging

import my_logging  # 導入自定義的logging配置

 

logger = logging.getLogger(__name__)  # 生成logger實例

 

 

def demo():

    logger.debug("start range... time:{}".format(time.time()))

    logger.info("中文測試開始。。。")

    for i in range(10):

        logger.debug("i:{}".format(i))

        time.sleep(0.2)

    else:

        logger.debug("over range... time:{}".format(time.time()))

    logger.info("中文測試結束。。。")

 

if __name__ == "__main__":

    my_logging.load_my_logging_cfg()  # 在你程序文件的入口加載自定義logging配置

    demo()

 
使用
###### !!!關於如何拿到logger對象的詳細解釋!!!

注意注意注意:

 

 

#一、有了上述方式咱們的好處是:全部與logging模塊有關的配置都寫到字典中就能夠了,更加清晰,方便管理

 

 

#二、咱們須要解決的問題是:

    1、從字典加載配置:logging.config.dictConfig(settings.LOGGING_DIC)

 

    2、拿到logger對象來產生日誌

    logger對象都是配置到字典的loggers 鍵對應的子字典中的

    按照咱們對logging模塊的理解,要想獲取某個東西都是經過名字,也就是key來獲取的

    因而咱們要獲取不一樣的logger對象就是

    logger=logging.getLogger('loggers子字典的key名')

 

   

    但問題是:若是咱們想要不一樣logger名的logger對象都共用一段配置,那麼確定不能在loggers子字典中定義n個key  

 'loggers': {   

        'l1': {

            'handlers': ['default', 'console'],  #

            'level': 'DEBUG',

            'propagate': True,  # 向上(更高level的logger)傳遞

        },

        'l2: {

            'handlers': ['default', 'console' ],

            'level': 'DEBUG',

            'propagate': False,  # 向上(更高level的logger)傳遞

        },

        'l3': {

            'handlers': ['default', 'console'],  #

            'level': 'DEBUG',

            'propagate': True,  # 向上(更高level的logger)傳遞

        },

 

}

 

   

#咱們的解決方式是,定義一個空的key

    'loggers': {

        '': {

            'handlers': ['default', 'console'],

            'level': 'DEBUG',

            'propagate': True,

        },

 

}

 

這樣咱們再取logger對象時

logging.getLogger(__name__),不一樣的文件__name__不一樣,這保證了打印日誌時標識信息不一樣,可是拿着該名字去loggers裏找key名時卻發現找不到,因而默認使用key=''的配置

 
關於如何拿到logger對象的詳細解釋!!!

另一個django的配置,瞄一眼就能夠,跟上面的同樣

#logging_config.py

LOGGING = {

    'version': 1,

    'disable_existing_loggers': False,

    'formatters': {

        'standard': {

            'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'

                      '[%(levelname)s][%(message)s]'

        },

        'simple': {

            'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

        },

        'collect': {

            'format': '%(message)s'

        }

    },

    'filters': {

        'require_debug_true': {

            '()': 'django.utils.log.RequireDebugTrue',

        },

    },

    'handlers': {

        #打印到終端的日誌

        'console': {

            'level': 'DEBUG',

            'filters': ['require_debug_true'],

            'class': 'logging.StreamHandler',

            'formatter': 'simple'

        },

        #打印到文件的日誌,收集info及以上的日誌

        'default': {

            'level': 'INFO',

            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切

            'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"),  # 日誌文件

            'maxBytes': 1024 * 1024 * 5,  # 日誌大小 5M

            'backupCount': 3,

            'formatter': 'standard',

            'encoding': 'utf-8',

        },

        #打印到文件的日誌:收集錯誤及以上的日誌

        'error': {

            'level': 'ERROR',

            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切

            'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"),  # 日誌文件

            'maxBytes': 1024 * 1024 * 5,  # 日誌大小 5M

            'backupCount': 5,

            'formatter': 'standard',

            'encoding': 'utf-8',

        },

        #打印到文件的日誌

        'collect': {

            'level': 'INFO',

            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切

            'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),

            'maxBytes': 1024 * 1024 * 5,  # 日誌大小 5M

            'backupCount': 5,

            'formatter': 'collect',

            'encoding': "utf-8"

        }

    },

    'loggers': {

        #logging.getLogger(__name__)拿到的logger配置

        '': {

            'handlers': ['default', 'console', 'error'],

            'level': 'DEBUG',

            'propagate': True,

        },

        #logging.getLogger('collect')拿到的logger配置

        'collect': {

            'handlers': ['console', 'collect'],

            'level': 'INFO',

        }

    },

}

 

 

# -----------

# 用法:拿到倆個logger

 

logger = logging.getLogger(__name__) #線上正常的日誌

collect_logger = logging.getLogger("collect") #領導說,須要爲領導們單獨定製領導們看的日誌

 

 
logging_config.py

11.2 正則模塊re

11.2.1 什麼是正則?

 正則就是用一些具備特殊含義的符號組合到一塊兒(稱爲正則表達式)來描述字符或者字符串的方法。或者說:正則就是用來描述一類事物的規則。(在Python中)它內嵌在Python中,並經過 re 模塊實現。正則表達式模式被編譯成一系列的字節碼,而後由用 C 編寫的匹配引擎執行。

生活中到處都是正則:

    好比咱們描述:4條腿

      你可能會想到的是四條腿的動物或者桌子,椅子等

    繼續描述:4條腿,活的

          就只剩下四條腿的動物這一類了

 

11.2.2 經常使用匹配模式(元字符)

http://blog.csdn.net/yufenghyc/article/details/51078107

 

 

 

11.2.3 匹配模式

# =================================匹配模式=================================

#一對一的匹配

# 'hello'.replace(old,new)

# 'hello'.find('pattern')

 

#正則匹配

import re

#\w與\W

print(re.findall('\w','hello egon 123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']

print(re.findall('\W','hello egon 123')) #[' ', ' ']

 

#\s與\S

print(re.findall('\s','hello  egon  123')) #[' ', ' ', ' ', ' ']

print(re.findall('\S','hello  egon  123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']

 

#\n \t都是空,均可以被\s匹配

print(re.findall('\s','hello \n egon \t 123')) #[' ', '\n', ' ', ' ', '\t', ' ']

 

#\n與\t

print(re.findall(r'\n','hello egon \n123')) #['\n']

print(re.findall(r'\t','hello egon\t123')) #['\n']

 

#\d與\D

print(re.findall('\d','hello egon 123')) #['1', '2', '3']

print(re.findall('\D','hello egon 123')) #['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' ']

 

#\A與\Z

print(re.findall('\Ahe','hello egon 123')) #['he'],\A==>^

print(re.findall('123\Z','hello egon 123')) #['he'],\Z==>$

 

#^與$

print(re.findall('^h','hello egon 123')) #['h']

print(re.findall('3$','hello egon 123')) #['3']

 

# 重複匹配:| . | * | ? | .* | .*? | + | {n,m} |

#.

print(re.findall('a.b','a1b')) #['a1b']

print(re.findall('a.b','a1b a*b a b aaab')) #['a1b', 'a*b', 'a b', 'aab']

print(re.findall('a.b','a\nb')) #[]

print(re.findall('a.b','a\nb',re.S)) #['a\nb']

print(re.findall('a.b','a\nb',re.DOTALL)) #['a\nb']同上一條意思同樣

 

#*

print(re.findall('ab*','bbbbbbb')) #[]

print(re.findall('ab*','a')) #['a']

print(re.findall('ab*','abbbb')) #['abbbb']

 

#?

print(re.findall('ab?','a')) #['a']

print(re.findall('ab?','abbb')) #['ab']

#匹配全部包含小數在內的數字

print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) #['123', '1.13', '12', '1', '3']

 

#.*默認爲貪婪匹配

print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b']

 

#.*?爲非貪婪匹配:推薦使用

print(re.findall('a.*?b','a1b22222222b')) #['a1b']

 

#+

print(re.findall('ab+','a')) #[]

print(re.findall('ab+','abbb')) #['abbb']

 

#{n,m}

print(re.findall('ab{2}','abbb')) #['abb']

print(re.findall('ab{2,4}','abbb')) #['abb']

print(re.findall('ab{1,}','abbb')) #'ab{1,}' ===> 'ab+'

print(re.findall('ab{0,}','abbb')) #'ab{0,}' ===> 'ab*'

 

#[]

print(re.findall('a[1*-]b','a1b a*b a-b')) #[]內的都爲普通字符了,且若是-沒有被轉意的話,應該放到[]的開頭或結尾

print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) #[]內的^表明的意思是取反,因此結果爲['a=b']

print(re.findall('a[0-9]b','a1b a*b a-b a=b')) #[]內的^表明的意思是取反,因此結果爲['a=b']

print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb')) #[]內的^表明的意思是取反,因此結果爲['a=b']

print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) #[]內的^表明的意思是取反,因此結果爲['a=b']

 

#\# print(re.findall('a\\c','a\c')) #對於正則來講a\\c確實能夠匹配到a\c,可是在python解釋器讀取a\\c時,會發生轉義,而後交給re去執行,因此拋出異常

print(re.findall(r'a\\c','a\c')) #r表明告訴解釋器使用rawstring,即原生字符串,把咱們正則內的全部符號都當普通字符處理,不要轉義

print(re.findall('a\\\\c','a\c')) #同上面的意思同樣,和上面的結果同樣都是['a\\c']

 

#():分組

print(re.findall('ab+','ababab123')) #['ab', 'ab', 'ab']

print(re.findall('(ab)+123','ababab123')) #['ab'],匹配到末尾的ab123中的ab

print(re.findall('(?:ab)+123','ababab123')) #findall的結果不是匹配的所有內容,而是組內的內容,?:可讓結果爲匹配的所有內容

print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">點擊</a>'))#['http://www.baidu.com']

print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">點擊</a>'))#['href="http://www.baidu.com"']

 

#|

print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))
匹配模式

11.2.4 re模塊提供的方法介紹

# ===========================re模塊提供的方法介紹===========================

import re

#1

print(re.findall('e','alex make love') )   #['e', 'e', 'e'],返回全部知足匹配條件的結果,放在列表裏

#2

print(re.search('e','alex make love').group()) #e,只到找到第一個匹配而後返回一個包含匹配信息的對象,該對象能夠經過調用group()方法獲得匹配的字符串,若是字符串沒有匹配,則返回None。

 

#3

print(re.match('e','alex make love'))    #None,同search,不過在字符串開始處進行匹配,徹底能夠用search+^代替match

 

#4

print(re.split('[ab]','abcd'))     #['', '', 'cd'],先按'a'分割獲得''和'bcd',再對''和'bcd'分別按'b'分割

 

#5

print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默認替換全部

print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love

print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love

print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex

 

print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),結果帶有總共替換的個數

 

 

#6

obj=re.compile('\d{2}')

 

print(obj.search('abc123eeee').group()) #12

print(obj.findall('abc123eeee')) #['12'],重用了obj
re模塊提供的方法介紹

11.2.5 補充一

#####補充一

import re

print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']

print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>

print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #<h1>hello</h1>

 

print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())

print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())
補充一

11.2.6 補充二

#####補充二

import re

 

print(re.findall(r'-?\d+\.?\d*',"1-12*(60+(-40.35/5)-(-4*3))")) #找出全部數字['1', '-12', '60', '-40.35', '5', '-4', '3']

 

 

#使用|,先匹配的先生效,|左邊是匹配小數,而findall最終結果是查看分組,全部即便匹配成功小數也不會存入結果

#而不是小數時,就去匹配(-?\d+),匹配到的天然就是,非小數的數,在此處即整數

print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出全部整數['1', '-2', '60', '', '5', '-4', '3']
補充二

11.2.7 計算器做業參考

#計算器做業參考:http://www.cnblogs.com/wupeiqi/articles/4949995.html

expression='1-2*((60+2*(-3-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'

 

content=re.search('\(([\-\+\*\/]*\d+\.?\d*)+\)',expression).group() #(-3-40.0/5)

11.2.8 search與findall

#爲什麼一樣的表達式search與findall卻有不一樣結果:

print(re.search('\(([\+\-\*\/]*\d+\.?\d*)+\)',"1-12*(60+(-40.35/5)-(-4*3))").group()) #(-40.35/5)

print(re.findall('\(([\+\-\*\/]*\d+\.?\d*)+\)',"1-12*(60+(-40.35/5)-(-4*3))")) #['/5', '*3']

 

#看這個例子:(\d)+至關於(\d)(\d)(\d)(\d)...,是一系列分組

print(re.search('(\d)+','123').group()) #group的做用是將全部組拼接到一塊兒顯示出來

print(re.findall('(\d)+','123')) #findall結果是組內的結果,且是最後一個組的結果
爲什麼一樣的表達式search與findall卻有不一樣結果
#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'

#在線調試工具:tool.oschina.net/regex/#

import re

 

s='''

http://www.baidu.com

egon@oldboyedu.com

你好

010-3141

'''

 

#最常規匹配

# content='Hello 123 456 World_This is a Regex Demo'

# res=re.match('Hello\s\d\d\d\s\d{3}\s\w{10}.*Demo',content)

# print(res)

# print(res.group())

# print(res.span())

 

#泛匹配

# content='Hello 123 456 World_This is a Regex Demo'

# res=re.match('^Hello.*Demo',content)

# print(res.group())

 

 

#匹配目標,得到指定數據

 

# content='Hello 123 456 World_This is a Regex Demo'

# res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)

# print(res.group()) #取全部匹配的內容

# print(res.group(1)) #取匹配的第一個括號內的內容

# print(res.group(2)) #去陪陪的第二個括號內的內容

 

 

 

#貪婪匹配:.*表明匹配儘量多的字符

# import re

# content='Hello 123 456 World_This is a Regex Demo'

#

# res=re.match('^He.*(\d+).*Demo$',content)

# print(res.group(1)) #只打印6,由於.*會盡量多的匹配,而後後面跟至少一個數字

 

 

#非貪婪匹配:?匹配儘量少的字符

# import re

# content='Hello 123 456 World_This is a Regex Demo'

#

# res=re.match('^He.*?(\d+).*Demo$',content)

# print(res.group(1)) #只打印6,由於.*會盡量多的匹配,而後後面跟至少一個數字

 

 

#匹配模式:.不能匹配換行符

content='''Hello 123456 World_This

is a Regex Demo

'''

# res=re.match('He.*?(\d+).*?Demo$',content)

# print(res) #輸出None

 

# res=re.match('He.*?(\d+).*?Demo$',content,re.S) #re.S讓.能夠匹配換行符

# print(res)

# print(res.group(1))

 

 

#轉義:\

 

# content='price is $5.00'

# res=re.match('price is $5.00',content)

# print(res)

#

# res=re.match('price is \$5\.00',content)

# print(res)

 

 

#總結:儘可能精簡,詳細的以下

    # 儘可能使用泛匹配模式.*

    # 儘可能使用非貪婪模式:.*?

    # 使用括號獲得匹配目標:用group(n)去取得結果

    # 有換行符就用re.S:修改模式

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#re.search:會掃描整個字符串,不會從頭開始,找到第一個匹配的結果就會返回

 

# import re

# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

#

# res=re.match('Hello.*?(\d+).*?Demo',content)

# print(res) #輸出結果爲None

 

#

# import re

# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

#

# res=re.search('Hello.*?(\d+).*?Demo',content) #

# print(res.group(1)) #輸出結果爲

 

 

 

#re.search:只要一個結果,匹配演練,

import re

content='''

<tbody>

<tr id="4766303201494371851675" class="even "><td><div class="hd"><span class="num">1</span><div class="rk"><span class="u-icn u-icn-75"></span></div></div></td><td class="rank"><div class="f-cb"><div class="tt"><a href="/song?id=476630320"><img class="rpic" src="http://p1.music.126.net/Wl7T1LBRhZFg0O26nnR2iQ==/19217264230385030.jpg?param=50y50&amp;quality=100"></a><span data-res-id="476630320" "

# res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)

# print(res.group(1))

 

 

#re.findall:找到符合條件的全部結果

# res=re.findall('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)

# for i in res:

#     print(i)

 

 

 

#re.sub:字符串替換

import re

content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

 

# content=re.sub('\d+','',content)

# print(content)

 

 

#用\1取得第一個括號的內容

#用法:將123與456換位置

# import re

# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

#

# # content=re.sub('(Extra.*?)(\d+)(\s)(\d+)(.*?strings)',r'\1\4\3\2\5',content)

# content=re.sub('(\d+)(\s)(\d+)',r'\3\2\1',content)

# print(content)

 

 

 

 

# import re

# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

#

# res=re.search('Extra.*?(\d+).*strings',content)

# print(res.group(1))

 

 

# import requests,re

# respone=requests.get('https://book.douban.com/').text

 

# print(respone)

# print('======'*1000)

# print('======'*1000)

# print('======'*1000)

# print('======'*1000)

# res=re.findall('<li.*?cover.*?href="(.*?)".*?title="(.*?)">.*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span.*?</li>',respone,re.S)

# # res=re.findall('<li.*?cover.*?href="(.*?)".*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span>.*?</li>',respone,re.S)

#

#

# for i in res:

#     print('%s    %s    %s   %s' %(i[0].strip(),i[1].strip(),i[2].strip(),i[3].strip()))

 

 
search與findall的使用

11.3 時間模塊time與datetime

11.3.1 時間模塊介紹

在Python中,一般有這幾種方式來表示時間:

l  時間戳(timestamp):一般來講,時間戳表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。咱們運行「type(time.time())」,返回的是float類型。

l  格式化的時間字符串(Format String)

l  結構化的時間(struct_time):struct_time元組共有9個元素共九個元素:(年,月,日,時,分,秒,一年中第幾周,一年中第幾天,夏令時)

 

11.3.2 時間模塊相關示例

import time

#--------------------------咱們先以當前時間爲準,讓你們快速認識三種形式的時間

print(time.time()) # 時間戳:1487130156.419527

print(time.strftime("%Y-%m-%d %X")) #格式化的時間字符串:'2017-02-15 11:40:53'

 

print(time.localtime()) #本地時區的struct_time

print(time.gmtime())    #UTC時區的struct_time

 
時間模塊示例

11.3.3 格式化字符串的時間格式

%a    Locale’s abbreviated weekday name.    

%A    Locale’s full weekday name.    

%b    Locale’s abbreviated month name.    

%B    Locale’s full month name.    

%c    Locale’s appropriate date and time representation.    

%d    Day of the month as a decimal number [01,31].    

%H    Hour (24-hour clock) as a decimal number [00,23].    

%I    Hour (12-hour clock) as a decimal number [01,12].    

%j    Day of the year as a decimal number [001,366].    

%m    Month as a decimal number [01,12].    

%M    Minute as a decimal number [00,59].    

%p    Locale’s equivalent of either AM or PM.    (1)

%S    Second as a decimal number [00,61].    (2)

%U    Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0.    (3)

%w    Weekday as a decimal number [0(Sunday),6].    

%W    Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.    (3)

%x    Locale’s appropriate date representation.    

%X    Locale’s appropriate time representation.     

%y    Year without century as a decimal number [00,99].    

%Y    Year with century as a decimal number.    

%z    Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].    

%Z    Time zone name (no characters if no time zone exists).    

%%    A literal '%' character.

 
格式化字符串的時間格式

11.3.4 時間戳轉換關係

其中計算機認識的時間只能是'時間戳'格式,而程序員可處理的或者說人類能看懂的時間有: '格式化的時間字符串','結構化的時間' ,因而有了下圖的轉換關係。

 

1 #--------------------------按圖1轉換時間

 2 # localtime([secs])

 3 # 將一個時間戳轉換爲當前時區的struct_time。secs參數未提供,則以當前時間爲準。

 4 time.localtime()

 5 time.localtime(1473525444.037215)

 6

 7 # gmtime([secs]) 和localtime()方法相似,gmtime()方法是將一個時間戳轉換爲UTC時區(0時區)的struct_time。

 8

 9 # mktime(t) : 將一個struct_time轉化爲時間戳。

10 print(time.mktime(time.localtime()))#1473525749.0

11

12

13 # strftime(format[, t]) : 把一個表明時間的元組或者struct_time(如由time.localtime()和

14 # time.gmtime()返回)轉化爲格式化的時間字符串。若是t未指定,將傳入time.localtime()。若是元組中任何一個

15 # 元素越界,ValueError的錯誤將會被拋出。

16 print(time.strftime("%Y-%m-%d %X", time.localtime()))#2016-09-11 00:49:56

17

18 # time.strptime(string[, format])

19 # 把一個格式化時間字符串轉化爲struct_time。實際上它和strftime()是逆操做。

20 print(time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X'))

21 #time.struct_time(tm_year=2011, tm_mon=5, tm_mday=5, tm_hour=16, tm_min=37, tm_sec=6,

22 #  tm_wday=3, tm_yday=125, tm_isdst=-1)

23 #在這個函數中,format默認爲:"%a %b %d %H:%M:%S %Y"。
按圖1轉換時間

 

 

 

1 #--------------------------按圖2轉換時間

2 # asctime([t]) : 把一個表示時間的元組或者struct_time表示爲這種形式:'Sun Jun 20 23:21:05 1993'。

3 # 若是沒有參數,將會將time.localtime()做爲參數傳入。

4 print(time.asctime())#Sun Sep 11 00:43:43 2016

5

6 # ctime([secs]) : 把一個時間戳(按秒計算的浮點數)轉化爲time.asctime()的形式。若是參數未給或者爲

7 # None的時候,將會默認time.time()爲參數。它的做用至關於time.asctime(time.localtime(secs))。

8 print(time.ctime())  # Sun Sep 11 00:46:38 2016

9 print(time.ctime(time.time()))  # Sun Sep 11 00:46:38 2016

1 #--------------------------其餘用法

2 # sleep(secs)

3 # 線程推遲指定的時間運行,單位爲秒。
按圖2轉換時間

11.3.5 datetime模塊

#時間加減

import datetime

 

# print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925

#print(datetime.date.fromtimestamp(time.time()) )  # 時間戳直接轉成日期格式 2016-08-19

# print(datetime.datetime.now() )

# print(datetime.datetime.now() + datetime.timedelta(3)) #當前時間+3天

# print(datetime.datetime.now() + datetime.timedelta(-3)) #當前時間-3天

# print(datetime.datetime.now() + datetime.timedelta(hours=3)) #當前時間+3小時

# print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #當前時間+30分

 

 

#

# c_time  = datetime.datetime.now()

# print(c_time.replace(minute=3,hour=2)) #時間替換

 
時間加減

第12章 做業:ATM+購物商城程序

做業需求:

模擬實現一個ATM + 購物商城程序

  1. 額度 15000或自定義
  2. 實現購物商城,買東西加入 購物車,調用信用卡接口結帳
  3. 能夠提現,手續費5%
  4. 每個月22號出帳單,每個月10號爲還款日,過時未還,按欠款總額 萬分之5 每日計息
  5. 支持多帳戶登陸
  6. 支持帳戶間轉帳
  7. 記錄每個月平常消費流水
  8. 提供還款接口
  9. ATM記錄操做日誌
  10. 提供管理接口,包括添加帳戶、用戶額度,凍結帳戶等。。。
  11. 用戶認證用裝飾器

示例代碼 https://github.com/triaquae/py3_training/tree/master/atm 

簡易流程圖:https://www.processon.com/view/link/589eb841e4b0999184934329 

相關文章
相關標籤/搜索