day15

六 模塊搜索路徑

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

#模塊的查找順序
一、在第一次導入某個模塊時(好比spam),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),若是有則直接引用
    ps:python解釋器在啓動時會自動加載一些模塊到內存中,可使用sys.modules查看
二、若是沒有,解釋器則會查找同名的內建模塊
三、若是尚未找到就從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文件,所以必定要事先建立他們,來避免加載模塊是性能降低。
複製代碼
#模塊的查找順序
一、在第一次導入某個模塊時(好比spam),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),若是有則直接引用
    ps:python解釋器在啓動時會自動加載一些模塊到內存中,可使用sys.modules查看
二、若是沒有,解釋器則會查找同名的內建模塊
三、若是尚未找到就從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文件,所以必定要事先建立他們,來避免加載模塊是性能降低。
複製代碼

官網解釋:python

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

七 編譯python文件(瞭解)

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

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

#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

八 包介紹

一、什麼是包?shell

#官網解釋
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. 建立包的目的不是爲了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包的本質就是一種模塊

二、爲什麼要使用包json

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

三、注意事項windows

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

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

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

四、上課流程api

1 實驗一
    準備:
        執行文件爲test.py,內容
        #test.py
        import aaa
        同級目錄下建立目錄aaa,而後自建空__init__.py(或者乾脆建包)

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

    解決:
        先執行看結果
        再在__init__.py添加打印信息後,從新執行

二、實驗二
    準備:基於上面的結果

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

三、實驗三
    準備:在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,先定義一個普通變量,再引出如何導入模塊名,強調:環境變量是以執行文件爲準
    

四、實驗四
    準備:在aaa下新建包bbb

    需求:
        aaa.bbb

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

五、實驗五
    準備:
        在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

六、實驗六
    準備:基於上面的結果

    需求:
        aaa.m1()
        aaa.m2()
        aaa.m3()
        進而實現
        aaa.f1()
        aaa.f2()
        aaa.f3()
        先用絕對導入,再用相對導入
        
    解決:在aaa的__init__.py中拿到名字m一、m二、m3
    包內模塊直接的相對導入,強調包的本質:包內的模塊是用來被導入的,而不是被執行的
    用戶沒法區分模塊是文件仍是一個包,咱們定義包是爲了方便開發者維護

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

九 包的使用

一、示範文件緩存

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)

包所包含的文件內容
複製代碼
#文件內容

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

包所包含的文件內容
複製代碼

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

二、包的使用之import 

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

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

#在與glance同級的test.py中
import glance
glance.cmd.manage.main()

'''
執行結果:
AttributeError: module 'glance' has no attribute 'cmd'

''' 

解決方法:

1 #glance/__init__.py
2 from . import cmd
3 
4 #glance/cmd/__init__.py
5 from . import manage

執行:

1 #在於glance同級的test.py中
2 import glance
3 glance.cmd.manage.main()

三、包的使用之from ... import ...

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

1 from glance.db import models
2 models.register_models('mysql')
3 
4 from glance.db.models import register_models
5 register_models('mysql')

四、from glance.api import *

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

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

1 #在__init__.py中定義
2 x=10
3 
4 def func():
5     print('from api.__init.py')
6 
7 __all__=['x','func','policy']

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

練習:

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

get()
create_resource('a.conf')
main()
register_models('mysql')
#執行文件中的使用效果以下,請處理好包的導入 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']
複製代碼

五、絕對導入和相對導入

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

絕對導入:以glance做爲起始

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

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

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

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

1 from glance.api import versions 

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

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

1 #在version.py中
2 
3 import policy
4 policy.get()

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

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

 1 from glance.api import versions
 2 
 3 '''
 4 執行結果:
 5 ImportError: No module named 'policy'
 6 '''
 7 
 8 '''
 9 分析:
10 此時咱們導入versions在versions.py中執行
11 import policy須要找從sys.path也就是從當前目錄找policy.py,
12 這必然是找不到的
13 '''

7 絕對導入與相對導入總結

絕對導入與相對導入

# 絕對導入: 以執行文件的sys.path爲起始點開始導入,稱之爲絕對導入
#        優勢: 執行文件與被導入的模塊中均可以使用
#        缺點: 全部導入都是以sys.path爲起始點,導入麻煩

# 相對導入: 參照當前所在文件的文件夾爲起始開始查找,稱之爲相對導入
#        符號: .表明當前所在文件的文件加,..表明上一級文件夾,...表明上一級的上一級文件夾
#        優勢: 導入更加簡單
#        缺點: 只能在導入包中的模塊時才能使用
      #注意:
        1. 相對導入只能用於包內部模塊之間的相互導入,導入者與被導入者都必須存在於一個包內
        2. attempted relative import beyond top-level package # 試圖在頂級包以外使用相對導入是錯誤的,言外之意,必須在頂級包內使用相對導入,每增長一個.表明跳到上一級文件夾,而上一級不該該超出頂級包

十 軟件開發規範

#===============>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][登陸成功]
複製代碼
#===============>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][登陸成功]
相關文章
相關標籤/搜索