1. 程式人生 > 實用技巧 >python模快的使用和包

python模快的使用和包

模組

1 什麼是模組?

常見的場景:一個模組就是一個包含了python定義和宣告的檔案,檔名就是模組名字加上.py的字尾。

但其實import載入的模組分為四個通用類別: 

  1 使用python編寫的程式碼(.py檔案)

  2 已被編譯為共享庫或DLL的C或C++擴充套件

  3 包好一組模組的包

  4 使用C編寫並連結到python直譯器的內建模組

2 為何要使用模組?

如果你退出python直譯器然後重新進入,那麼你之前定義的函式或者變數都將丟失,因此我們通常將程式寫到檔案中以便永久儲存下來,需要時就通過python test.py方式去執行,此時test.py被稱為指令碼script。

隨著程式的發展,功能越來越多,為了方便管理,我們通常將程式分成一個個的檔案,這樣做程式的結構更清晰,方便管理。這時我們不僅僅可以把這些檔案當做指令碼去執行,還可以把他們當做模組來匯入到其他的模組中,實現了功能的重複利用

3.如何使用模組?

import 匯入模組

示例檔案:自定義模組my_module.py,檔名my_module.py,模組名my_module

#my_module.py
print('from the my_module.py')

money=1000

def read1():
print('my_module->read1->money',money)

def read2():

print('my_module->read2 calling read1')
read1()

def change():
global money
money=0

模組可以包含可執行的語句和函式的定義,這些語句的目的是初始化模組,它們只在模組名第一次遇到匯入import語句時才執行(import語句是可以在程式中的任意位置使用的,且針對同一個模組很import多次,為了防止你重複匯入,python的優化手段是:第一次匯入後就將模組名載入到記憶體了,後續的import語句僅是對已經載入大記憶體中的模組物件增加了一次引用,不會重新執行模組內的語句),如下

#demo.py
import my_module #只在第一次匯入時才執行my_module.py內程式碼,此處的顯式效果是隻列印一次'from the my_module.py',當然其他的頂級程式碼也都被執行了,只不過沒有顯示效果.

import my_module
import my_module
import my_module
'''
執行結果:
from the my_module.py
'''

我們可以從sys.modules中找到當前已經載入的模組,sys.modules是一個字典,內部包含模組名與模組物件的對映,該字典決定了匯入模組時是否需要重新匯入。

每個模組都是一個獨立的名稱空間,定義在這個模組中的函式,把這個模組的名稱空間當做全域性名稱空間,這樣我們在編寫自己的模組時,就不用擔心我們定義在自己模組中全域性變數會在被匯入時,與使用者的全域性變數衝突

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

#測試一:money與my_module.money不衝突
#demo.py
import my_module
money=10
print(my_module.money)

'''
執行結果:
from the my_module.py
1000
'''

測試二:read1與my_module.read1不衝突

#測試二:read1與my_module.read1不衝突
#demo.py
import my_module
def read1():
print('========')
my_module.read1()

'''
執行結果:
from the my_module.py
my_module->read1->money 1000
'''

測試三:執行my_module.change()操作的全域性變數money仍然是my_module中的

#測試三:執行my_module.change()操作的全域性變數money仍然是my_module中的
#demo.py
import my_module
money=1
my_module.change()
print(money)

'''
執行結果:
from the my_module.py
1
'''

總結:首次匯入模組my_module時會做三件事:

1.為原始檔(my_module模組)建立新的名稱空間,在my_module中定義的函式和方法若是使用到了global時訪問的就是這個名稱空間。

2.在新建立的名稱空間中執行模組中包含的程式碼,見初始匯入importmy_module

  1. 提示:匯入模組時到底執行了什麼?
  2. 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.
  3. 事實上函式定義也是“被執行”的語句,模組級別函式定義的執行將函式名放入模組全域性名稱空間表,用globals()可以檢視

3.建立名字my_module來引用該名稱空間

這個名字和變數名沒什麼區別,都是‘第一類的’,且使用my_module.名字的方式可以訪問my_module.py檔案中定義的名字,my_module.名字與test.py中的名字來自兩個完全不同的地方。

為模組名起別名,相當於m1=1;m2=m1

1 import my_module as sm
2 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()
複製程式碼

示範用法二:

為已經匯入的模組起別名的方式對編寫可擴充套件的程式碼很有用,假設有兩個模組xmlreader.py和csvreader.py,它們都定義了函式read_data(filename):用來從檔案中讀取一些資料,但採用不同的輸入格式。可以編寫程式碼來選擇性地挑選讀取模組,例如

例項用法2

if file_format == 'xml':
import xmlreader as reader
elif file_format == 'csv':
import csvreader as reader
data=reader.read_date(filename)

在一行匯入多個模組

import sys,os,re 但是不推薦使用這種方式

from ... import...

對比import my_module,會將原始檔的名稱空間'my_module'帶到當前名稱空間中,使用時必須是my_module.名字的方式

而from 語句相當於import,也會建立新的名稱空間,但是將my_module中的名字直接匯入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了、

from my_module import read1,read2

這樣在當前位置直接使用read1和read2就好了,執行時,仍然以my_module.py檔案全域性名稱空間

#測試一:匯入的函式read1,執行時仍然回到my_module.py中尋找全域性變數money
#demo.py
from my_module import read1
money=1000
read1()
'''
執行結果:
from the my_module.py
spam->read1->money 1000
'''

#測試二:匯入的函式read2,執行時需要呼叫read1(),仍然回到my_module.py中找read1()
#demo.py
from my_module import read2
def read1():
print('==========')
read2()

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

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

#測試三:匯入的函式read1,被當前位置定義的read1覆蓋掉了
#demo.py
from my_module import read1
def read1():
print('==========')
read1()
'''
執行結果:
from the my_module.py
==========
'''

需要特別強調的一點是:python中的變數賦值不是一種儲存操作,而只是一種繫結關係,如下:

from my_module import money,read1
money=100 #將當前位置的名字money繫結到了100
print(money) #列印當前的名字
read1() #讀取my_module.py中的名字money,仍然為1000
'''
from the my_module.py
100
my_module->read1->money 1000
'''

也支援as

from my_module import read1 as read

也支援匯入多行

from my_module import (read1, read2, money)

from my_moduleimport * 把my_module中所有的不是以下劃線(_)開頭的名字都匯入到當前位置,大部分情況下我們的python程式不應該使用這種匯入方式,因為*你不知道你匯入什麼名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在互動式環境中匯入時沒有問題。

from my_module import * #將模組my_module中所有的名字都匯入到當前名稱空間
print(money)
print(read1)
print(read2)
print(change)

'''
執行結果:
from the my_module.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''

在my_module.py中新增一行

__all__=['money','read1'] #這樣在另外一個檔案中用from my_module import *就這能匯入列表中規定的兩個名字

from 模組名 import * 和 __all__是一對

 沒有這個變數,就會匯入所有的名字
如果有all值匯入all列表中的名字

模組的迴圈引用問題
思考:假如有兩個模組a,b。我可不可以在a模組中import b ,再在b模組中import a?
模組的載入與修改

考慮到效能的原因,每個模組只被匯入一次,放入字典sys.modules中,如果你改變了模組的內容,你必須重啟程式,python不支援重新載入或解除安裝之前匯入的模組,

有的同學可能會想到直接從sys.modules中刪除一個模組不就可以解除安裝了嗎,注意了,你刪了sys.modules中的模組物件仍然可能被其他程式的元件所引用,因而不會被清除。

特別的對於我們引用了這個模組中的一個類,用這個類產生了很多物件,因而這些物件都有關於這個模組的引用。

如果只是你想互動測試的一個模組,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這隻能用於測試環境。

把模組當做指令碼執行

我們可以通過模組的全域性變數__name__來檢視模組名:
當做指令碼執行:
__name__ 等於'__main__'

當做模組匯入:
__name__=模組名

作用:用來控制.py檔案在不同的應用場景下執行不同的邏輯
if __name__ == '__main__':

def fib(n):
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()

if __name__ == "__main__":
print(__name__)
num = input('num :')
fib(int(num))

模組搜尋路徑

python直譯器在啟動時會自動載入一些模組,可以使用sys.modules檢視

在第一次匯入某個模組時(比如my_module),會先檢查該模組是否已經被載入到記憶體中(當前執行檔案的名稱空間對應的記憶體),如果有則直接引用

如果沒有,直譯器則會查詢同名的內建模組,如果還沒有找到就從sys.path給出的目錄列表中依次尋找my_module.py檔案。

所以總結模組的查詢順序是:記憶體中已經載入的模組->內建模組->sys.path路徑中包含的模組

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,路徑放到前面的優先於標準庫被載入。

import sys
sys.path.append('/a/b/c/d')
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檔案,因此一定要事先建立他們,來避免載入模組是效能下降。

官網解釋

#官網連結:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
搜尋路徑:
當一個命名為my_module的模組被匯入時
直譯器首先會從內建模組中尋找該名字
找不到,則去sys.path中找該名字

sys.path從以下位置初始化
執行檔案所在的當前目錄
PTYHONPATH(包含一系列目錄名,與shell變數PATH語法一樣)
依賴安裝時預設指定的

注意:在支援軟連線的檔案系統中,執行指令碼所在的目錄是在軟連線之後被計算的,換句話說,包含軟連線的目錄不會被新增到模組的搜尋路徑中

在初始化後,我們也可以在python程式中修改sys.path,執行檔案所在的路徑預設是sys.path的第一個目錄,在所有標準庫路徑的前面。這意味著,當前目錄是優先於標準庫目錄的,需要強調的是:我們自定義的模組名不要跟python標準庫的模組名重複,除非你是故意的。

編譯python檔案

為了提高載入模組的速度,強調強調強調:提高的是載入速度而絕非執行速度。python直譯器會在__pycache__目錄中下快取每個模組編譯後的版本,格式為:module.version.pyc。通常會包含python的版本號。例如,在CPython3.3版本下,my_module.py模組會被快取成__pycache__/my_module.cpython-33.pyc。這種命名規範保證了編譯後的結果多版本共存。

Python檢查原始檔的修改時間與編譯的版本進行對比,如果過期就需要重新編譯。這是完全自動的過程。並且編譯的模組是平臺獨立的,所以相同的庫可以在不同的架構的系統之間共享,即pyc使一種跨平臺的位元組碼,類似於JAVA火.NET,是由python虛擬機器來執行的,但是pyc的內容跟python的版本相關,不同的版本編譯後的pyc檔案不同,2.5編譯的pyc檔案不能到3.5上執行,並且pyc檔案是可以反編譯的,因而它的出現僅僅是用來提升模組的載入速度的。

python直譯器在以下兩種情況下不檢測快取
  1 如果是在命令列中被直接匯入模組,則按照這種方式,每次匯入都會重新編譯,並且不會儲存編譯後的結果(python3.3以前的版本應該是這樣)

python -m my_module.py

2 如果原始檔不存在,那麼快取的結果也不會被使用,如果想在沒有原始檔的情況下來使用編譯後的結果,則編譯後的結果必須在源目錄下

提示:

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

補充:dir()函式

內建函式dir是用來查詢模組中定義的名字,返回一個有序字串列表

import my_module

dir(my_module)

如果沒有引數,dir()列舉出當前定義的名字

dir()不會列舉出內建函式或者變數的名字,它們都被定義到了標準模組builtin中,可以列舉出它們,

import builtins
dir(builtins)

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

1. 無論是import形式還是from...import形式,凡是在匯入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關於包才有的匯入語法

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

3. import匯入檔案時,產生名稱空間中的名字來源於檔案,import 包,產生的名稱空間的名字同樣來源於檔案,即包下的__init__.py,匯入包本質就是在匯入該檔案

強調:

  1. 在python3中,即使包下沒有__init__.py檔案,import 包仍然不會報錯,而在python2中,包下一定要有該檔案,否則import 包報錯

  2. 建立包的目的不是為了執行,而是被匯入使用,記住,包只是模組的一種形式而已,包即模組

包A和包B下有同名模組也不會衝突,如A.a與B.a來自倆個名稱空間

建立目錄程式碼

import os
os.makedirs('glance/api')
os.makedirs('glance/cmd')
os.makedirs('glance/db')
l = []
l.append(open('glance/__init__.py','w'))
l.append(open('glance/api/__init__.py','w'))
l.append(open('glance/api/policy.py','w'))
l.append(open('glance/api/versions.py','w'))
l.append(open('glance/cmd/__init__.py','w'))
l.append(open('glance/cmd/manage.py','w'))
l.append(open('glance/db/models.py','w'))
map(lambda f:f.close() ,l)

目錄結構

#檔案內容

#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)

注意事項

1.關於包相關的匯入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什麼位置,在匯入時都必須遵循一個原則:凡是在匯入時帶點的,點的左邊都必須是一個包,否則非法。可以帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。

2.對於匯入後,在使用時就沒有這種限制了,點的左邊可以是包,模組,函式,類(它們都可以用點的方式呼叫自己的屬性)。

3.對比import item 和from item import name的應用場景:
如果我們想直接使用name那必須使用後者

import

我們在與包glance同級別的檔案中測試

import glance.db.models
glance.db.models.register_models('mysql') models模組下的函式可以正常呼叫
# from models.py: mysql

from ... import ...

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

我們在與包glance同級別的檔案中測試

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

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

__init__.py檔案
不管是哪種方式,只要是第一次匯入包或者是包的任何其他部分,都會依次執行包下的__init__.py檔案
(我們可以在每個包的檔案內都列印一行內容來驗證一下),這個檔案可以為空,但是也可以存放一些初始化包的程式碼。
from glance.api import *

在講模組時,我們已經討論過了從一個模組內匯入所有*,此處我們研究從一個包匯入所有*。

此處是想從包api中匯入所有,實際上該語句只會匯入包api下__init__.py檔案中定義的名字,我們可以在這個檔案中定義__all___:

#在__init__.py中定義
x=10

def func():
print('from api.__init.py')

__all__=['x','func','policy']

此時我們在於glance同級的檔案中執行from glance.api import *就匯入__all__中的內容(versions仍然不能匯入)

from glance.api import *

glance/

├── __init__.py

├── api

│ ├── __init__.py __all__ = ['policy','versions']

│ ├── policy.py

│ └── versions.py

├── cmd __all__ = ['manage']

│ ├── __init__.py

│ └── manage.py

└── db __all__ = ['models']

├── __init__.py

└── models.py

from glance.api import *
policy.get()

絕對匯入和相對匯入

我們的最頂級包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

注意:在使用pycharm時,有的情況會為你多做一些事情,這是軟體相關的東西,會影響你對模組匯入的理解,因而在測試時,一定要回到命令列去執行,模擬我們生產環境,你總不能拿著pycharm去上線程式碼吧!!!

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

比如我們想在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,
這必然是找不到的
'''

絕對匯入

glance/

├── __init__.py from glance import api
from glance import cmd
from glance import db

├── api

│ ├── __init__.py from glance.api import policy
from glance.api import versions

│ ├── policy.py

│ └── versions.py

├── cmd from glance.cmd import manage

│ ├── __init__.py

│ └── manage.py

└── db from glance.db import models

├── __init__.py

└── models.py

相對匯入

glance/

├── __init__.py from . import api #.表示當前目錄
from . import cmd from . import db

├── api

│ ├── __init__.py from . import policy
from . import versions

│ ├── policy.py

│ └── versions.py

├── cmd from . import manage

│ ├── __init__.py

│ └── manage.py from ..api import policy
#..表示上一級目錄,想再manage中使用policy中的方法就需要回到上一級glance目錄往下找api包,從api匯入policy

└── db from . import models

├── __init__.py

└── models.py

單獨匯入包

單獨匯入包名稱時不會匯入包中所有包含的所有子模組,如

#在與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()

千萬別問:__all__不能解決嗎,__all__是用於控制from...import *

import glance之後直接呼叫模組中的方法

import glance

glance/

├── __init__.py from .api import *
from .cmd import *
from .db import *
├── api

│ ├── __init__.py __all__ = ['policy','versions']

│ ├── policy.py

│ └── versions.py

├── cmd __all__ = ['manage']

│ ├── __init__.py

│ └── manage.py

└── db __all__ = ['models']

├── __init__.py

└── models.py

import glance
policy.get()

軟體開發規範

開始檔案中獲取路徑模組

import os
import sys
sys.path.append(os.path.dirname(os.getcwd()))
from core import core
if __name__ == '__main__'
core.main()

#=============>bin目錄:存放執行指令碼
#start.py
import sys,os

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

from core import core
from conf import my_log_settings

if __name__ == '__main__':
    my_log_settings.load_my_logging_cfg()
    core.run()

#=============>conf目錄:存放配置檔案
#config.ini
[DEFAULT]
user_timeout = 1000

[egon]
password = 123
money = 10000000

[alex]
password = alex3714
money=10000000000

[yuanhao]
password = ysb123
money=10

#settings.py
import os
config_path=r'%s\%s' %(os.path.dirname(os.path.abspath(__file__)),'config.ini')
user_timeout=10
user_db_path=r'%s\%s' %(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),\
                     'db')


#my_log_settings.py
"""
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 = r'%s\log' %os.path.dirname(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()

#=============>core目錄:存放核心邏輯
#core.py
import logging
import time
from conf import settings
from lib import read_ini

config=read_ini.read(settings.config_path)
logger=logging.getLogger(__name__)

current_user={'user':None,'login_time':None,'timeout':int(settings.user_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>>: ')
        if config.has_section(name):
            if password == config.get(name,'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('''
購物
檢視餘額
轉賬
    ''')
    while True:
        choice = input('>>: ').strip()
        if not choice:continue
        if choice == '1':
            buy()



if __name__ == '__main__':
    run()

#=============>db目錄:存放資料庫檔案
#alex_json
#egon_json

#=============>lib目錄:存放自定義的模組與包
#read_ini.py
import configparser
def read(config_file):
    config=configparser.ConfigParser()
    config.read(config_file)
    return config

#=============>log目錄:存放日誌
#all2.log
[2017-07-29 00:31:40,272][MainThread:11692][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:31:41,789][MainThread:11692][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
[2017-07-29 00:31:46,394][MainThread:12348][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:31:47,629][MainThread:12348][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
[2017-07-29 00:31:57,912][MainThread:10528][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:03,340][MainThread:12744][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:05,065][MainThread:12916][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:08,181][MainThread:12916][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
[2017-07-29 00:32:13,638][MainThread:7220][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:23,005][MainThread:7220][task_id:core.core][core.py:20][INFO][登入成功]
[2017-07-29 00:32:40,941][MainThread:7220][task_id:core.core][core.py:20][INFO][登入成功]
[2017-07-29 00:32:47,222][MainThread:7220][task_id:core.core][core.py:20][INFO][登入成功]
[2017-07-29 00:32:51,949][MainThread:7220][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
[2017-07-29 00:33:00,213][MainThread:7220][task_id:core.core][core.py:20][INFO][登入成功]
[2017-07-29 00:33:50,118][MainThread:8500][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:33:55,845][MainThread:8500][task_id:core.core][core.py:20][INFO][登入成功]
[2017-07-29 00:34:06,837][MainThread:8500][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
[2017-07-29 00:34:09,405][MainThread:8500][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
[2017-07-29 00:34:10,645][MainThread:8500][task_id:core.core][core.py:25][ERROR][使用者名稱不存在]
開發規範例子