day5-python之模塊和包

1、模塊

序言

1.什麼是模塊?

常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。html

import加載的模塊分爲四個通用類別:python

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

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

  3. 包好一組模塊的包shell

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

2.爲什麼要使用模塊?

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

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

3.如何使用模塊?

1) import

示例文件:自定義模塊my_module.py,文件名my_module.py,模塊名my_module。api

一個py文件就能夠做爲一個模塊。緩存

模塊的導入:直接導入文件的名字,不須要帶後綴。

模塊中的函數調用:模塊名.函數名()

#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是一個字典,內部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否須要從新導入。

import sys
for i in sys.modules:  #查看是否導入過這個模塊
    print(i)

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

#測試一: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不衝突
#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中的
#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. 在新建立的命名空間中執行模塊中包含的代碼,見初始導入import my_module。

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

    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. 建立名字my_module來引用該命名空間
    這個名字和變量名沒什麼區別,都是‘第一類的’,且使用my_module.名字的方式能夠訪問my_module.py文件中定義的名字,my_module.名字與test.py中的名字來自兩個徹底不一樣的地方。

爲模塊名起別名,至關於m1=1;m2=m1

import my_module as sm
print(sm.money)
  • 首先開闢一個新的命名空間my_module
  • 執行my_module內代碼
  • 將my_module裏面的名字和命名空間綁定在一塊兒

示例方法一:

有兩種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)用來從文件中讀取一些數據,但採用不一樣的輸入格式。能夠編寫代碼來選擇性地挑選讀取模塊

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

導入的模塊有本身的命名空間(能夠給導入的模塊起一個別名,就產生了一個命名空間,這個命名空間只和別名相關)

import  my_moudle  as mm
    print(mm.money)

2) 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_module import * 把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 *就這能導入列表中規定的兩個名字

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

考慮到性能的緣由,每一個模塊只被導入一次,放入字典sys.modules中,若是改變了模塊的內容,必須重啓程序,python不支持從新加載或卸載以前導入的模塊。

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

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

# aa.py
def func1():
    print('func1')

# 測試代碼
import time,importlib
import aa
 
time.sleep(20)
# importlib.reload(aa)
aa.func1()

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

打開importlib註釋,從新測試

3) 把模塊當作腳本執行

咱們能夠經過模塊的全局變量__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))

4) 模塊搜索路徑

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標準庫的模塊名重複,除非你是故意的,傻叉。

5) 編譯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

6)補充:dir()函數

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

import my_module
dir(my_module)

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

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

import builtins
dir(builtins)

2、包

包是一種經過使用‘.模塊名’來組織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)

# 目錄結構
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)

注意事項:

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

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

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

1.import

咱們在與包glance同級別的文件中測試

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

2.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')

3.__init__.py
無論是哪一種方式,只要是第一次導入包或者是包的任何其餘部分,都會依次執行包下的__init__.py文件(咱們能夠在每一個包的文件內都打印一行內容來驗證一下),這個文件能夠爲空,可是也能夠存放一些初始化包的代碼。

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']

此時咱們在於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()

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

注意:在使用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

6.單獨導入包

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

#在與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 *

(1) 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()

3、軟件開發規範

#=============>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][用戶名不存在]
相關文章
相關標籤/搜索