【Python之路Day5】基礎篇

今日目錄html

多層裝飾器python

迭代器和生成器linux

遞歸git

字符串格式化github

模塊 web

序列化相關模塊docker

time、datetime模塊shell

logging模塊數據庫

 

一. 多層裝飾器json

仍是上一篇的那個例子,關於用戶管理程序:登陸用戶管理程序,查看用戶信息的時候,系統要提示登陸,登陸驗證成功後普通用戶能夠查看本身信息,管理員登陸後才能夠進入管理界面,普通用戶提示權限不足,這樣一來,就能夠從新寫下程序,來兩個裝飾器來裝飾。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)


#先定義一個用戶字典,判斷用戶狀態,用戶身份等
USER_INFO = {'user_state': False, 'admin_flag':2}

#搞一個裝飾器,裝飾管理後臺,檢查用戶身份等
def outer(func):
    def inner(*args,**kwargs):
        if USER_INFO['user_state']:
            result = func(*args,**kwargs)
            return result
        else:
            print('你沒有登錄系統,請登錄後再操做!')
    return inner

#在定義一個check_manager裝飾器,用戶檢測用戶登陸身份是管理員仍是普通用戶
def check_manager(func):
    def inner(*args, **kwargs):
        if USER_INFO['admin_flag'] == 0:
            result = func(*args,**kwargs)
            return result
        else:
            print('權限不足!')
    return inner

#管理後臺函數被裝飾的時候,從下到上渲染,解釋執行的時候是從上到下執行,因此先使用outer裝飾, 再使用check_manager
@outer
@check_manager
def manager():
    print('歡迎登錄到管理員界面')

#檢查用戶身份函數
@outer
def checkuser(username):
    if USER_INFO['admin_flag'] == 0:
        print('{} 您的用戶是管理員身份'.format(username))
    else:
        print('{} 普通用戶啦'.format(username))

#登陸函數
def login(username,userpwd):
    if username == 'tom' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 0
        manager()
    elif username == 'jerry' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 1
    else:
        print('用戶名或者密碼錯誤!')
        #print('歡迎登錄 %s' %username)

#主函數
def main():
    while True:
        print('1: 管理後臺   2: 登錄   3. 檢查用戶身份  4. 退出')
        user_input_num = input('選擇下吧:').strip()
        if user_input_num == '1':
            result = manager()
        elif user_input_num == '2':
            username = input('請輸入您的用戶名:').strip()
            userpwd = input('密碼').strip()
            login(username,userpwd)
        elif user_input_num == '3':
            checkuser(USER_INFO.get('current_user',None))
        elif user_input_num == '4':
            print('Bye')
            break

if __name__ == '__main__':
    main()

使用兩個裝飾器裝飾一個函數
使用兩個裝飾器裝飾一個函數

使用一個多層裝飾器來裝飾同一個用戶身份、權限等;

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)


#先定義一個用戶字典,判斷用戶狀態,用戶身份等
USER_INFO = {'user_state': False, 'admin_flag':2}

#搞一個裝飾器,裝飾管理後臺,檢查用戶身份等
def outer(func):
    def inner(*args,**kwargs):
        if USER_INFO['user_state']:
            if USER_INFO['admin_flag'] == 0:
                result = func(*args,**kwargs)
                return result
            else:
                print('權限不足!')
        else:
            print('你沒有登錄系統,請登錄後再操做!')
    return inner

# 在定義一個check_manager裝飾器,用戶檢測用戶登陸身份是管理員仍是普通用戶
# def check_manager(func):
#     def inner(*args, **kwargs):
#         if USER_INFO['admin_flag'] == 0:
#             result = func(*args,**kwargs)
#             return result
#         else:
#             print('權限不足!')
#     return inner

#只使用一個裝飾器裝飾
@outer
def manager():
    print('歡迎登錄到管理員界面')

#檢查用戶身份函數
@outer
def checkuser(username):
    if USER_INFO['admin_flag'] == 0:
        print('{} 您的用戶是管理員身份'.format(username))
    else:
        print('{} 普通用戶啦'.format(username))

#登陸函數
def login(username,userpwd):
    if username == 'tom' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 0
        manager()
    elif username == 'jerry' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 1
    else:
        print('用戶名或者密碼錯誤!')
        #print('歡迎登錄 %s' %username)

#主函數
def main():
    while True:
        print('1: 管理後臺   2: 登錄   3. 檢查用戶身份  4. 退出')
        user_input_num = input('選擇下吧:').strip()
        if user_input_num == '1':
            result = manager()
        elif user_input_num == '2':
            username = input('請輸入您的用戶名:').strip()
            userpwd = input('密碼').strip()
            login(username,userpwd)
        elif user_input_num == '3':
            checkuser(USER_INFO.get('current_user',None))
        elif user_input_num == '4':
            print('Bye')
            break

if __name__ == '__main__':
    main()

使用多層裝飾器
使用多層裝飾器

注意,使用多層裝飾器,渲染的順序是從下往上,而解釋是從上往下。正如上面例子中‘使用兩個裝飾器裝飾一個函數’同樣,先使用@outer裝飾,再使用@check_manager來裝飾。解釋的時候是先解釋outer,然後再是check_manager。所以執行的時候若是沒有登陸,會先提示登陸,知足後,驗證權限。

 

二. 迭代器和生成器

1. 迭代器

迭代器,首先來講是也是一個對象。它知道如何從一個對象中一次取出一個元素,而且跟蹤它當前所在序列的位置。還記得以前的文章中反覆提到的for循環遍歷一個可迭代的對象麼?好比說列表:

l1 = [11,22,33,44,55,]
for i in l1:
    print(i)

#執行上面代碼返回:
11
22
33
44
55

再好比說,字符串:

name = 'daniel'
for i in name:
    print(i)

#執行返回:
d
a
n
i
e
l

或者元組,字典:

#元組
t1 = (11,22,33,44,55)
for i in t1:
    print(i)

#返回
11
22
33
44
55

#字典
d1 = {'name':'daniel','Age':18}
for i in d1:
    print(i)
#返回:
name
Age

其實在後臺,for循環對對象執行了iter()函數,iter()是Python的內置函數,定義了__next__()方法的迭代器對象,在容器中逐個訪問對象中的元素,取完最後一個值以後,會拋出異常(StopIteration), 通知for循環結束。好比下面的方法,定義一個字符串s1 = 'daniel':

>>> s1 = 'daniel'
#字符串自己沒有__next__方法
>>> s1.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__next__'

#調用iter() 封裝s1,並賦值給name
>>> name = iter(s1)

#查看類型,此時變成一個迭代器(str_iterator)
>>> type(name)
<class 'str_iterator'>
#使用__next__()方法逐個調用
>>> name.__next__()
'd'
>>> name.__next__()
'a'
>>> name.__next__()
'n'
>>> name.__next__()
'i'
>>> name.__next__()
'e'
>>> name.__next__()
'l'
#所有取完元素以後,會拋異常!(StopIteration)
>>> name.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> l1 = [11,22,33,44,55,]
>>> l1.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'
>>> result = iter(l1)
>>> type(result)
<class 'list_iterator'>


>>> result.__next__()
11
>>> result.__next__()
22
>>> result.__next__()
33
>>> result.__next__()
44
>>> result.__next__()
55
>>> result.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
列表其餘也都同樣

因此,iter()只會被調用一次,而__next__()會被調用N次!

2. 生成器

生成器是使用函數創造,若是函數裏出現了 'yield' 關鍵字,這個函數就成爲一個生成器。以下代碼:

#函數f1,裏面使用了yield關鍵字
def f1():
    print('f1')
    yield 11
    yield 22
    yield 33

#查看類型,就已是一個生成器了!
print(type(f1()))

#輸出:
<class 'generator'>

然後可使用__next__()方法調用,每次進入函數找到yield關鍵字,取出yield後面的數據。而且記錄此次的位置,下一次還從當前位置開始。

print(result.__next__())
print('-'*20)
print(result.__next__())
print('-'*20)
print(result.__next__())
print('-'*20)

#打印結果以下:
f1
11
--------------------
22
--------------------
33
--------------------

#若是還繼續的話,也會拋異常()
print(result.__next__())
print('-'*20)

StopIteration
def f1():
    print('f1')
    yield 11
    yield 22
    yield 33

result = f1()
for i in result:
    print(i)
也可使用再使用for遍歷

或者本身定義一個myrange的函數

def myrange(args):
    start_value = 0
    while 1:
        if start_value > args:
            return
        else:
            yield start_value
            start_value += 1

#接受用戶輸入的一個終止值
user_input=input('一個終止值:').strip()
#賦值,並傳給myrange
result = myrange(int(user_input))

#循環遍歷打印result
for i in result:
    print(i)

3. 迭代器和生成器的區別

每一個生成器都是一個迭代器,可是反過來就不行。生成器能作到迭代器所作的全部的事兒,並且生成器顯得特別簡潔,並且也是高效的。

一個帶yield關鍵字的函數就是一個生成器,它和普通函數不一樣,生成一個 generator 看起來像是函數調用,可是不會執行任何函數代碼,直到對其調用 __next__()纔開始執行(或者for循環)。

二者在取完對象,在調用取用時都會拋出 StopIteration 的異常。

生成器是經過調用一個或者多個yield表達式構成的而且知足迭代器的定義。

二者的相同點: 對象迭代完成後就不能再重寫迭代了

 

三. 遞歸

在函數內部,能夠調用其餘函數:

def a():
    return 'a'

def b():
    r = a()
    return r

def c():
    r = b()
    return r

def d():
    r = c()
    return r

result = d()
print(result)

若是一個函數在內部調用本身自己,那麼這個函數就是一個遞歸函數。如:

def testf(func):
    func += 1
    if func > 4:
        print(func)
        return
    return testf(func)

testf(1)

#執行結果,返回
5
遞歸函數

思考題:

  使用遞歸函數方式計算: 1 * 2*3*4*5*6*7的值。

def sikao(func):
    if func == 7:   #若是傳func等於7,return func
        return func
    return func * sikao(func+1)   #調用函數自身,用func乘以func+1值

result = sikao(1)  #將1做爲實際參數傳給函數
print(result)        #打印值
思考題

 

四. 字符串格式化

 1. %方式

%方式是相對較老的字符串格式化方式,語法格式是:

%[(name)][flags][width].[precision]typecode

解釋以下:

name:(可選) 命名,用於字典型的控制賦值

>>> s1 = 'My name is %(name)s' %{'name':'daniel'}
>>> print(s1)
My name is daniel

flags(可選):須要配合width使用,值能夠是:

+   右對齊, 正數前面加正號,負數前面加上負號

-    左對齊,正數前面無符號,負數前面加負號

0    右對齊,正數前面加上空格,負數前面加上負號,數字時用0填充;

空格  右對齊, 正數前面加空格,負數前面加負號

>>> s1 = 'My name is %+20s' %'daniel'
>>> print(s1)
My name is               daniel
>>> s1 = 'My name is %+20s age: %-10d' %('daniel',18)
>>> print(s1)
My name is               daniel age: 18 

#空格,右對齊,正數前面加空格,負數前面加負號
>>> s1 = 'My name is % 20s age: %- 20d, money: %- 10d' %('daniel',18,-10)
>>> print(s1)
My name is               daniel age:  18                 , money: -10

#0, 右對齊方式,正數前面無負號,負數前面加負號,在數字前面用0填充
>>> s1 = 'My name is %020s age: %020d, money: %010d' %('daniel',18,-10)
>>> print(s1)
My name is               daniel age: 00000000000000000018, money: -000000010

width (可選項)   寬度,總長度

>>> print('%10s'%'name')
      name
>>> print('%10d'%20)
        20
>>> print('%-10d'%20)
20        
>>> print('%+10d'%20)
       +20
>>> print('%010d'%20)
0000000020
>>> print('% 10d'%20)
        20

precision(可選項):  精度,若是數字是浮點數的話,能夠設定顯示數字的顯示精度,如四捨五入到2位小數點:

>>> print('%.2f'%3.1315926)
3.13

typecode(必選項), 這塊主要是選擇數據類型的,經常使用的以下:

%s  字符串,獲取對象str()方法的返回值

>>> print('%s' %'name')
name

%r  字符串,獲取對象repr()方法的返回值

>>> name = '%r'%'name'
>>> type(name)
<class 'str'>
>>> name
"'name'"
%r

%c  單個字符, 將數字轉換爲unicode對應的值(還記得上個文章說的ASCII表麼?),10進制範圍爲0 <=i <= 1114111

>>> print('%c' %a)
A
>>> a = 66
>>> print('%c' %a)
B
>>> a = 89
>>> print('%c' %a)
Y
%c

 

%d  十進制整數

>>> a = 20
>>> print('%d'%a)
20
>>> print('%d'%a)
20
>>> a = 25
>>> print('%d'%a)
25
%a

%i   十進制整數

>>> print('%i'%a)
25
>>> a = 89
>>> print('%i'%a)
89
>>> a = 8901
>>> print('%i'%a)
8901
%i

%o  八進制整數, 將整數轉換爲8進製表示

>>> a = 8901
>>> print('%o'%a)
21305
>>> a = 10
>>> print('%o'%a)
12
%o

%x  十六進制整數, 將整數轉換爲16進製表示

>>> a = 10
>>> print('%x'%a)
a
>>> a = 15
>>> print('%x'%a)
f
>>> a = 12330
>>> print('%x'%a)
302a
%x

%e  指數(小e)

>>> a = 12330
>>> print('%e'%a)
1.233000e+04
%e

%E  指數(大寫E)

>>> a = 12330
>>> print('%E'%a)
1.233000E+04
%E

%f   浮點數,默認顯示到小數點後6位

>>> a = 3.1415926
>>> print('%f'%a)
3.141593
%f

%F  浮點數,默認顯示到小數點後6位

>>> a = 3.1415926
>>> print('%F'%a)
3.141593
%F

%g  指數e或浮點數(根據長度顯示)

>>> a = 1300882
>>> print('%g'%a)
1.30088e+06
%g

%G  指數E或浮點數(根據長度顯示)

>>> a = 1300882
>>> print('%G'%a)
1.30088E+06
%G

注意,要想在%方式格式化字符串中輸出%,必需要使用%%來轉譯!

>>> print('%s balabala %%'%'name')
name balabala %

若是在格式化輸出的時候加#,在o、i、d、x前面會添加對應的進製表示,如:

>>> a = 123
>>> print('%#o' %a)
0o173
>>> print('%#x' %a)
0x7b
#十進制輸出不變
>>> print('%#d' %a)
123
>>> print('%#i' %a)
123
View Code

 

2. format方式

format格式是比較新的字符串格式化方式,大有替代%s的方式,不過目前官方沒有明確表示要替換掉%s. 相比%s方式,format更增強大;

格式:

[[fill]align][sign][#][0][width][,][.precision][type]
  • fill(可選項)   空白處填充的字符,須要配合width使用
  • >>> print('{:#^30} balabala'.format('tianchong'))
    ##########tianchong########### balabala
    View Code
  • align(可選項)   對其方式,也須要配合width使用

<    左對齊

>    右對齊

=    右對齊, 將符號放在填充字符的左側,並且只是對數字類型生效,若是使用字符串會報錯!

^    居中顯示

#居中顯示,並以#號填充
>>> print('{:#^30} balabala'.format('tianchong'))
##########tianchong########### balabala
#左對齊
>>> print('{:#<30}'.format('tianchong'))
tianchong#####################
#右對齊
>>> print('{:#>30}'.format('tianchong'))
#####################tianchong
#=使用字符串會報錯
>>> print('{:#=30}'.format('tianchong'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: '=' alignment not allowed in string format specifier
#使用數字ok
>>> print('{:#=30}'.format(600))
###########################600
View Code
  • sign(可選項)   有無符號數字

+  正加正、負加負

-   正不變、負加負

空格  正好空格, 負加負

#+, 正加正,負加負
>>> print('{:#^+30}'.format(600))
#############+600#############
#-, 正不變,負加負
>>> print('{:#^-30}'.format(600))
#############600##############
#空格, 正加空格,負加負
>>> print('{:#^ 30}'.format(600))
############# 600#############
View Code
  • #(可選項)       對二進制、八進制、十六進制,加上後會顯示 0b 0o 0x,默認不顯示
  • >>> print('{:o}'.format(600))
    1130
    #加上#號,會在二進制、八進制、十六進制前面打印對應的進制標識
    >>> print('{:#o}'.format(600))
    0o1130
    >>> print('{:#x}'.format(600))
    0x258
    >>> print('{:#b}'.format(600))
    0b1001011000
    >>> print('{:#d}'.format(600))
    600
    View Code
  • , (可選項)       爲數字添加分隔符, 如財務計算金錢,或者銀行帳戶顯示 1,322,220,220
  • >>> print('{:,d}'.format(203301010293))
    203,301,010,293
    View Code
  • width(可選項)      寬度
  • #通常都是配合fill、align和sign一塊兒使用
    >>> print('{:10,d}'.format(203301010293))
    203,301,010,293
    View Code
  • .precision(可選項)   小數點精度
  • #默認顯示小數點位數後面6位
    >>> print('{:f}'.format(3.1415926))
    3.141593
    >>> print('{:F}'.format(3.1415926))
    3.141593
    
    #能夠指定精度
    >>> print('{:.2F}'.format(3.1415926))
    3.14
    >>> print('{:.2f}'.format(3.1415926))
    3.14
    View Code
  • type (可選項)     格式化類型 

  字符串類型的參數:

字符型:

  s    字符串類型

空白   未指定類型,默認是None,同於s

整數型:

b   10進制轉換爲2進制並實現格式化

c    10進制轉換爲unicode字符,仍是上個文章對應的那個ASCII表

d    10進制整數

o    將10進制轉換爲8進制

x    10進制轉換爲16進制,小x表示

X    10進制轉換爲16進制,大X表示

浮點型參數:

e  科學技術法,用小e表示

E  科學技術法,大E

>>> print('{:e}'.format(1415926))
1.415926e+06
>>> print('{:E}'.format(1415926))
1.415926E+06
>>> print('{:g}'.format(1415926))
1.41593e+06
>>> print('{:G}'.format(1415926))
1.41593E+06
e/E/g/G

f   浮點數,默認保留小數點後6位

F   浮點數,默認保留小數點後6位

g   自動在e和f中切換,若是是e表示爲小e

G  自動在E和F中切換,若是是E表示爲大E

% 顯示百分比,默認也是小數點後6位

>>> print('{:%}'.format(0.7926))
79.260000%
>>> print('{:.2%}'.format(0.7926))
79.26%
%

 

五. 模塊

簡單的說,模塊就是包含了一組Python代碼的文件,模塊裏能夠是類、函數、變量(如配置文件),也能夠是一組可執行代碼。

Python中的模塊有內置模塊、第三方模塊和自定義模塊;

1. 內置模塊

安裝完Python以後,Python自帶了一部分經常使用的模塊(庫),供開發者使用,這就是內置模塊,也叫做標準模塊,或者標準庫。好比: sys,os,time,datetime,json等等...

個人Mac上的安裝路徑在:

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5

裏面所有.py的文件(小的)或者文件夾(大的),查看路徑方式以下:

>>> import sys
>>> result = sys.path

>>> for i in result:
...   print(i)
... 

/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages
View Code

使用這些模塊是,先import導入,然後再使用便可!

2. 第三方模塊

Python 社區有着牛逼到爆的第三方模塊,提供給開發者使用,加速開發速度,直接用現成的輪子便可,不須要重複造輪子。聽說能夠知足完成全部能想到的事情(別鑽牛角尖哈),甚至你想不到的也是ok的。地址 https://pypi.python.org/pypi

安裝第三方模塊:

安裝第三方模塊的方式簡單的不行,主要有兩種,一種是經過setuptools工具完成;另外一種是下載源碼而後安裝。

先來看下setuptools

Python中有兩種工具都封裝了setuptools包管理工具,一個是easy_install; 一個是pip(python2叫pip,python3叫pip3,而且python3自帶pip3),推薦使用pip。(固然python3得用pip3哈,不要鑽牛角尖哈)

  • 使用pip3安裝一個模塊wrfy:
root@test3-ubunut:~# pip3 install wrfy
Downloading/unpacking wrfy
  Downloading wrfy-0.1.0.tar.gz
  Running setup.py (path:/tmp/pip_build_root/wrfy/setup.py) egg_info for package wrfy
    
Downloading/unpacking docker-py==1.8.1 (from wrfy)
  Downloading docker_py-1.8.1-py2.py3-none-any.whl (41kB): 41kB downloaded
Downloading/unpacking progressbar2==3.5.0 (from wrfy)
  Downloading progressbar2-3.5.0.tar.gz
  Running setup.py (path:/tmp/pip_build_root/progressbar2/setup.py) egg_info for package progressbar2
    
    warning: no files found matching 'AUTHORS.rst'
    warning: no files found matching 'README.txt'
Downloading/unpacking requests>=2.5.2 (from docker-py==1.8.1->wrfy)
  Downloading requests-2.10.0-py2.py3-none-any.whl (506kB): 506kB downloaded
Downloading/unpacking websocket-client>=0.32.0 (from docker-py==1.8.1->wrfy)
  Downloading websocket_client-0.37.0.tar.gz (194kB): 194kB downloaded
  Running setup.py (path:/tmp/pip_build_root/websocket-client/setup.py) egg_info for package websocket-client
    
Downloading/unpacking backports.ssl-match-hostname>=3.5 (from docker-py==1.8.1->wrfy)
  Downloading backports.ssl_match_hostname-3.5.0.1.tar.gz
  Running setup.py (path:/tmp/pip_build_root/backports.ssl-match-hostname/setup.py) egg_info for package backports.ssl-match-hostname
    
Requirement already satisfied (use --upgrade to upgrade): six>=1.4.0 in /usr/lib/python3/dist-packages (from docker-py==1.8.1->wrfy)
Installing collected packages: wrfy, docker-py, progressbar2, requests, websocket-client, backports.ssl-match-hostname
  Running setup.py install for wrfy
    
    Installing wrfy script to /usr/local/bin
  Running setup.py install for progressbar2
    
    warning: no files found matching 'AUTHORS.rst'
    warning: no files found matching 'README.txt'
  Found existing installation: requests 2.2.1
    Not uninstalling requests at /usr/lib/python3/dist-packages, owned by OS
  Running setup.py install for websocket-client
    changing mode of build/scripts-3.4/wsdump.py from 644 to 755
    
    changing mode of /usr/local/bin/wsdump.py to 755
  Running setup.py install for backports.ssl-match-hostname
    
Successfully installed wrfy docker-py progressbar2 requests websocket-client backports.ssl-match-hostname
Cleaning up...
pip3 install wrfy
  • 源碼安裝:

先下載源碼到目錄,然後解壓:

root@test2-ubunut:~# tar xf mycloud-0.51.tar.gz 

root@test2-ubunut:~# cd mycloud-0.51/

安裝:

python3 setup.py install 
  • 使用安裝的模塊:
#先導入模塊import
>>> import wrfy
#然後就可使用了
>>> dir(wrfy)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
View Code

3. 自定義模塊:

還記得上週做業麼,學習了函數,也開始嘗試使用函數式做業完成需求,寫完整個做業以後,發現有500多行代碼。要想作個編輯啥的,維護起來就費勁了。

好吧,這個500多行若是說還好的話,那麼若是程序碼了50000行呢?50W,500W呢?顯而易見,一個文件是不行的,就應該按照模塊的觀念,將相同功能的模塊放在一個文件中,各個文件之間經過導入來調用。如Python自帶的time庫就是提供了全部關於time的方法等等。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

'''
Author: DBQ
Blog: http://www.cnblogs.com/dubq/articles/5543466.html
Github: https://github.com/daniel-vv/ops
'''

import time
import re
import os


USER_STATUS = {'user_status':False,'username':False,'user_type':False} #全局用戶狀態字典變量, user_status: 登陸成功後會置爲True
                                                                       # username:  登陸成功後會將值置爲用戶名,便於在後面引用
                                                                       # user_type: 默認普通用戶爲False,管理員登陸置爲True
LOCK_FILE = 'locked.db'  #黑名單數據庫文件
USERDB = 'passwd'        #用戶數據庫文件
USERDB_NEW = 'passwd.new' #用戶臨時文件
LOCK_FILE_NEW = 'locked.db.new'

def validate_email(email):
    '''
    檢測用戶輸入郵箱地址合法性
    :param email: 接受用戶傳入的形式參數
    :return: 地址合法返回True,不然爲False
    '''
    if len(email)>7:
        if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",email) != None:
            return True
    return False

def valid_phone(phone):
    '''
    合法性檢測,主要檢測用戶輸入電話號碼是否合法
    :param phone: 接受形式參數
    :return: True合法,False不合法
    '''
    if len(phone) == 11:
        if re.match("[1]{1}[3,5,7,8,9]{1}[0-9]{9}",phone) != None:
            return True
    return False

def valid_str(string):
    '''
    合法性檢測,主要檢測用戶用戶名是否合法,必須是字母開頭,而且大於兩位的字符,不包含特殊字符
    :param phone: 接受形式參數
    :return: True合法,False不合法
    '''
    if len(string) != 0:
        if re.match("^[a-zA-Z][a-zA-Z0-9]{1,10}$",string) != None:
            return True
    return False

def check_user(func):
    '''
    檢查用戶是否登陸裝飾器,主要裝飾修改密碼,查詢用戶,刪除用戶,提示權限四個模塊
    :param func:
    :return:
    '''
    def inner(*args,**kwgras):
        if  USER_STATUS['username']:
            result = func(*args,**kwgras)
            return result
        else:
            if input('\033[31;1m您尚未登陸,瞎逛什麼呢,登陸再說!!按下任意鍵繼續...\033[0m'):pass
    return inner


def login():
    '''
    用戶登陸函數,輸入密碼錯誤三次,而且是同一個用戶的話,將鎖定這個用戶
    :return: True,表示認證成功, 還會返回用戶名username
    '''
    Count = 0
    user_init = ''
    user_lock_count = 1
    flag = False
    while Count<3:
        username = input('請輸入您的用戶名:').strip()
        if username:
            password = input('請輸入您的密碼: ').strip()
            with open(LOCK_FILE,'r') as lockfile:
                for i in lockfile:
                    if username == i.strip():
                        print('\033[31;1m抱歉, [ {} ]用戶已經被鎖定, 請聯繫管理員解鎖!\033[0m'.format(username))
                        return False

            if username == user_init:
                user_lock_count += 1
                # print(user_lock_count,username,user_init)
            else:
                user_init = username
                user_lock_count -= 1
                # print(user_lock_count,username,user_init)
            with open(USERDB,'r') as f:
                for i in f:
                    user,passwd,user_type = i.strip().split(':')[0],i.strip().split(':')[1],i.strip().split(':')[5]
                    if username == user and password == passwd:
                        print('\033[32;1m[ {} ] 歡迎登陸用戶管理系統\033[0m'.format(username))
                        flag = True
                        break
                else:
                    print('用戶名或密碼錯誤!')

            if flag:  #如何認證成功,更改默認全局變量裏的用戶屬性信息,並返回True
                USER_STATUS['user_status'] = True
                USER_STATUS['username'] = username
                USER_STATUS['user_type'] = True if user_type == '0' else False  #爲True表示用戶是管理員身份,寫到字典裏待進一步判斷
                return True
            if not flag:
                Count += 1
                continue
        else:
            continue
    else:
        # print(user_lock_count)
        # print(username,user_init)
        if user_lock_count == 2:  #若是用戶計數器累加到2,鎖定用戶到黑名單文件
            with open(LOCK_FILE,'a+') as f:
                print('\033[31;1m十分抱歉的告訴您,輸入次數超限,[ {} ]用戶已經被鎖定,請聯繫管理員解鎖!'.format(username))
                f.write(username+'\n')
                time.sleep(1)


def registry_user():
    '''
    註冊函數
    :return: 註冊成功返回True和記錄值, 失敗返回False和記錄值
    '''
    flag = False if not USER_STATUS['user_status'] else True
    Count = 0
    if flag and not USER_STATUS['user_type']: #若是是普通用戶登陸的狀態下選擇註冊用戶,
        user_input = input('\033[31;1m{} 您已經登陸了,可是普通用戶沒法添加用戶,你能夠嘗試退出帳戶,然後註冊一個用戶, 肯定退出登陸麼? y/Y\033[0m'.format(USER_STATUS['username'])).strip()
        if user_input == 'y' or user_input == 'Y':
            flag = False
            USER_STATUS['user_status'] = False  #在字典裏將標誌位置爲否表示退出用戶登陸
            USER_STATUS['username'] = False
            if input('\033[31;1m您已退出登陸,按下任意鍵繼續...\033[0m'):pass
        else:
            return False,None
    if flag and USER_STATUS['user_type']:  #若是用戶已經登陸,而且是管理員權限的話,直接添加用戶
        flag = False
    while not flag:
        username = input('輸入一個個性的名字吧:\033[31;1m(必填項)\033[0m  ').strip()
        if not valid_str(username):
            print('您輸入的用戶名不合法, 須要字母或者字母+數字組合,不能包含特殊字符,不能是數字開頭,不能少於3個字符,謝謝.')
            continue
        else:
            Count += 1
        password = input('來一個牛逼一點的密碼,前提是你能記得住哈:\033[31;1m(必填項)\033[0m  ').strip()
        password_verify = input('來吧,重複一遍你牛逼的密碼:\033[31;1m(必填項)\033[0m  ').strip()
        if password != password_verify:
            print('\033[31;1m你輸入的兩次牛逼的密碼不一致!\033[0m')
            continue
        else:
            Count += 1

        if not password or not password_verify:
            continue
        else:
            Count += 1
        homedir = input('輸入你的用戶家目錄, 默認/home/username').strip()
        if not homedir:homedir = '/home/{}'.format(username)
        mailaddr = input('輸入你的郵箱地址:\033[31;1m(必填項,格式:username@domain.com)\033[0m ').strip()
        if not validate_email(mailaddr):
            print('\033[31;1m郵箱輸入不合法,我作了合法性檢測,不要糊弄我!!!\033[0m')
            continue
        else:
            Count += 1
        user_shell = input('輸入你的shell, 默認/bin/bash').strip()
        user_shell = user_shell if user_shell else '/bin/bash'
        user_status = 1
        user_phone = input('請輸入電話號碼,方便通知你有妹子找你.\033[31;1m(必填項)\033[0m ').strip()
        if not valid_phone(user_phone):  #調用電話號碼合法性檢測
            print('\033[31;1m輸入的電話號碼不合法,我作了合法性檢測,不要糊弄我!!!\033[0m')
            continue
        else:
            Count += 1

        if Count >= 5:
            break

    record = '{}:{}:{}:{}:{}:{}:{}'.format(username,password,homedir,mailaddr,user_shell,user_status,user_phone)
    with open(USERDB,'r') as f:
        for i in f:
            if i.strip().split(':')[0] == username:
                if input('\033[31;1m用戶名{}太受歡迎,已經被註冊啦,再換一個吧,老兄! 按下任意鍵繼續...\033[0m'.format(username)):pass
                return False,record
    return True,record


@check_user
def get_user(username):
    '''
    查詢用戶信息函數
    :param username: 接受用戶參數
    :return: 暫時沒用到
    '''

    if USER_STATUS['user_type']:
        user_input = input('\033[31;1m哇哇哇,管理員吶,請輸入你的查詢, 僅限於郵箱模糊查詢,如(gmail.com)\033[0m   ').strip()
        with open('passwd','r') as f:
            flag = False
            for i in f:
                if user_input in i:
                    # print('True')
                    username,homedir = i.strip().split(':')[0],i.strip().split(':')[2]
                    emailaddr,user_shell,user_phone = i.strip().split(':')[3],i.strip().split(':')[4],i.strip().split(':')[6]
                    user_type = '管理員' if i.strip().split(':')[5] == '0' else '普通用戶'
                    print('您模糊搜索到的包含{}的用戶:'.center(50,'+').format(user_input))
                    print('''
                        用戶名:      {}
                        家目錄:      {}
                        郵箱地址:    {}
                        用戶Shell:   {}
                        用戶類型:    {}
                        聯繫電話:    {}
                    '''.format(username,homedir,emailaddr,user_shell,user_type,user_phone))
                    print('+'*63)
                    if input('\033[32;1m按下任意鍵繼續...\033[0m'):pass
                    flag = True
            else:
                if not flag:
                    print('\033[31;1m沒有匹配到包含 %s 的用戶信息\033[0m'%user_input)


    else:
        # print(USER_STATUS['user_type'])
        print('\033[32;1m普通用戶只能查看本身的信息啦,想看其餘人,得提高權限,找管理員哈!\033[0m')
        with open(USERDB,'r') as f:
            for i in f:
                # print(i.strip().split(':')[0])
                user = i.strip().split(':')[0]
                homedir = i.strip().split(':')[2]
                email = i.strip().split(':')[3]
                userbash = i.strip().split(':')[4]
                phone = i.strip().split(':')[6]
                user_status = '管理員' if i.strip().split(':')[5] == '0' else '普通用戶'
                if i.strip().split(':')[0] == username:
                   print('''
                      \033[32;1m[{}] 用戶信息\033[0m
                   用戶名:        {}
                   家目錄:        {}
                   郵箱地址:      {}
                   用戶shell:     {}
                   用戶類型:      {}
                   聯繫電話:      {}
                   '''.format(user,user,homedir,email,userbash,user_status,phone))
                   if input('\033[32;1m按下任意鍵繼續...\033[0m'):pass


@check_user
def edit_password(username):
    '''
    修改密碼函數
    :param username: 接受用戶傳入形式參數用戶名
    :return: True: 更改密碼成功   False: 更改失敗!
    '''
    flag = False
    Count = 0
    if not USER_STATUS['user_type']:  #若是用戶是普通用戶身份,只能修改本身密碼
        with open(USERDB,'r') as readonly_file ,open(USERDB_NEW,'w+') as write_file:
            for i in readonly_file: #遍歷循環,並把每一個值賦給變量,準備後面驗證後拼接;
                # print(i.strip().split(':')[0])
                user,password = i.strip().split(':')[0],i.strip().split(':')[1],
                userdir,usermail = i.strip().split(':')[2],i.strip().split(':')[3],
                user_shell,user_type,user_phone = i.strip().split(':')[4],i.strip().split(':')[5],i.strip().split(':')[6]
                if username != user:
                    write_file.write(i.strip()+'\n')
                else:
                    password_new = input('請輸入您的新密碼: ').strip()
                    password_new_verify = input('請再次輸入您的新密碼: ').strip()
                    if password_new == password_new_verify:  #作一次密碼校驗,驗證兩次輸入是否一致
                        write_file.write('{}:{}:{}:{}:{}:{}:{}\n'.format(user,password_new,userdir,usermail,user_shell,user_type,user_phone))
                        if input('\033[32;1m密碼更改爲功!任意鍵繼續...\033[0m'):pass
                        flag = True
                    else:
                        if input('\033[31;1m兩次輸入的密碼不一致!任意鍵繼續...\033[0m'):pass
                        return False
                Count += 1
        if flag:
        # os.rename(USERDB_NEW,USERDB)   #若是更改爲功,直接把老文件更名爲源文件,實現改密碼,並返回True
            with open(USERDB_NEW,'r') as f_new, open(USERDB,'w+') as f_old:
                for i in f_new.readlines():
                    if not i.split():
                        continue
                    else:
                        f_old.write(i)
            return True

    else: #若是用戶身份是管理員的話,將能夠更改用戶自身密碼,還有其餘普通用戶的密碼;
        user_input = input('\033[31;1m哇哇哇,管理員吶,您要改誰的密碼? 1:當前用戶  2.其餘用戶\033[0m   ').strip()
        if user_input == '1':
            with open(USERDB,'r') as readonly_file ,open(USERDB_NEW,'w+') as write_file:
                for i in readonly_file: #遍歷循環,並把每一個值賦給變量,準備後面驗證後拼接;
                    user,password = i.strip().split(':')[0],i.strip().split(':')[1]
                    userdir,usermail = i.strip().split(':')[2],i.strip().split(':')[3],
                    user_shell,user_type,user_phone = i.strip().split(':')[4],i.strip().split(':')[5],i.strip().split(':')[6]
                    if username != user:
                        write_file.write(i.strip()+'\n')
                    else:
                        password_new = input('請輸入您的新密碼: ').strip()
                        password_new_verify = input('請再次輸入您的新密碼: ').strip()
                        if password_new == password_new_verify:  #作一次密碼校驗,驗證兩次輸入是否一致
                            write_file.write('{}:{}:{}:{}:{}:{}:{}\n'.format(user,password_new,userdir,usermail,user_shell,user_type,user_phone))
                            if input('\033[32;1m密碼更改爲功!任意鍵繼續...\033[0m'):pass
                            flag = True
                        else:
                            if input('\033[31;1m兩次輸入的密碼不一致!任意鍵繼續...\033[0m'):pass
                            return False

        if user_input == '2':
            user_input_name = input('\033[31;1m您要改誰的密碼? 輸入一個要更改密碼的用戶名: \033[0m   ').strip()
            # print(user_input_name)
            user_list = []
            username = user_input_name  #將用戶輸入的用戶名賦值給username變量
            with open(USERDB,'r') as f:
                for user in f:
                    user_db = user.strip().split(':')[0]
                    user_list.append(user_db)
            if username in user_list:
                with open(USERDB,'r') as readonly_file ,open(USERDB_NEW,'w+') as write_file:
                    for i in readonly_file: #遍歷循環,並把每一個值賦給變量,準備後面驗證後拼接;
                        user = i.strip().split(':')[0]
                        userdir,usermail = i.strip().split(':')[2],i.strip().split(':')[3]
                        user_shell,user_type,user_phone = i.strip().split(':')[4],i.strip().split(':')[5],i.strip().split(':')[6]
                        if username != user:
                            write_file.write(i.strip()+'\n')
                        else:
                            password_new = input('請輸入您的新密碼: ').strip()
                            password_new_verify = input('請再次輸入您的新密碼: ').strip()
                            if password_new == password_new_verify:  #作一次密碼校驗,驗證兩次輸入是否一致
                                write_file.write('{}:{}:{}:{}:{}:{}:{}\n'.format(user,password_new,userdir,usermail,user_shell,user_type,user_phone))
                                if input('\033[32;1m密碼更改爲功!任意鍵繼續...\033[0m'):pass
                                flag = True
                            else:
                                if input('\033[31;1m兩次輸入的密碼不一致!任意鍵繼續...\033[0m'%username):pass
                                return False
            else:
                print('\033[31m你輸入的用戶名[ %s ]不存在!\033[0m')
                return False

    if flag:
        # os.rename(USERDB_NEW,USERDB)   #若是更改爲功,直接把老文件更名爲源文件,實現改密碼,並返回True
        a = 0
        with open(USERDB_NEW,'r') as f_new, open(USERDB,'w+') as f_old:
            for i in f_new:
                if a == 0:
                    f_old.write(i.strip())
                else:
                    f_old.write('\n'+i.strip())
                a += 1
        return True


@check_user
def update_user():
    '''
    提高用戶權限函數,主要用戶將普通管理員提高爲管理員權限
    :return:
    '''
    username_list = [] #定義一個空列表,用戶把全部的用戶名抓到裏面來,來判斷提高用戶權限的用戶名是否存在
    flag = False
    if USER_STATUS['user_type']:
        username = input('\033[32;1m管理員,你要提高誰的權限爲管理員? 來吧, 告訴我他/她的名字: \033[0m').strip()
        with open(USERDB,'r') as f:  #遍歷文件,把全部用戶名追加到列表中
            for i in f:
                user = i.strip().split(':')[0]
                username_list.append(user)
        if username not in username_list:  #若是用戶名不存在的話,提示用戶用戶名不存在
            print('\033[31;1m滾粗,用戶名根本不存在,逗誰呢!\033[0m')
            return False
        else:
            with open(USERDB,'r') as readfile,open(USERDB_NEW,'w+') as writefile:
                for line in readfile:
                    user_name,password,homedir = line.strip().split(':')[0],line.strip().split(':')[1],line.strip().split(':')[2]
                    emailaddr,user_shell,user_phone = line.strip().split(':')[3],line.strip().split(':')[4],line.strip().split(':')[6]
                    if username != user_name:
                        writefile.write(line.strip()+'\n')
                    else:
                        user_type = '0' if username == user_name else '1'
                        record = '{}:{}:{}:{}:{}:{}:{}\n'.format(user_name,password,homedir,emailaddr,user_shell,user_type,user_phone)
                        writefile.write(record)
                        print('\033[31;1m%s用戶已經被你提高爲管理員,哈!\033[0m'%username)
                        flag = True
    if flag:
        os.rename(USERDB_NEW,USERDB)
        time.sleep(1)
        return True


@check_user
def unlock_user():
    '''
    解鎖用戶函數,主要是管理員使用
    :return: 成功返回True,不然爲False
    '''
    user_list = []
    flag = False
    if USER_STATUS['user_type']:  #若是用戶是管理員能夠執行這個
        user_input_name = input('請輸入要解鎖的用戶名:').strip()
        if user_input_name:
            with open(LOCK_FILE,'r') as f,open(LOCK_FILE_NEW,'w+') as f1:
                for i in f:
                    if user_input_name == i.strip():
                        flag = True
                        continue
                    else:
                        f1.write(i)

        else:
            return False
    else:
        if input('\033[32;1m只有管理員能夠解鎖用戶,普通用戶不可進行此操做!\033[0m'):pass
        return False
    if flag:
        print('解鎖成功!')
        os.rename(LOCK_FILE_NEW,LOCK_FILE)
        return True
    if not flag:
        print('\033[31;1m用戶名[ %s ]不在黑名單列表中!\033[0m'%user_input_name)
        return False

@check_user
def del_user():
    '''
    刪除用戶函數
    :return: 用戶刪除成功返回True, 失敗爲False
    '''
    flag = False
    username_list = [] #定義一個空列表,用戶把全部的用戶名抓到裏面來,來判斷提用戶是否存在
    if not USER_STATUS['user_type']:
        if input('\033[31;1m[ %s ],你不是管理員啦,誰也沒法刪除!\033[0m'%USER_STATUS['username']):pass
    else:
        user_input_name = input('\033[31;1m管理員[ %s ]先生/女士, 你要刪誰? 給我個名字:  \033[0m'%USER_STATUS['username']).strip()
        if user_input_name:
            with open(USERDB,'r') as f_list:  #遍歷文件,把全部用戶名追加到列表中
                for i in f_list:
                    user = i.strip().split(':')[0]
                    username_list.append(user)
            if user_input_name in username_list:
                with open(USERDB,'r') as f,open(USERDB_NEW,'w+') as f1:
                    for line in f:
                        user_name,password,homedir = line.strip().split(':')[0],line.strip().split(':')[1],line.strip().split(':')[2]
                        emailaddr,user_shell,user_phone = line.strip().split(':')[3],line.strip().split(':')[4],line.strip().split(':')[6]
                        if user_input_name == user_name:
                            continue
                        else:
                            f1.write(line)
                            flag = True
            else:
                print('\033[31;1m抱歉, 你輸入的用戶名[ %s ]不存在!\033[0m '%user_input_name)
                return False
    if flag:
        os.rename(USERDB_NEW,USERDB)
        time.sleep(1)
        if input('\033[32;1m[ %s ]已經被刪除!\033[0m '%user_input_name):pass
        return True


def print_fun():
    if USER_STATUS['username']:
        print('\033[35;1m歡迎 [%s] 來到用戶管理系統\033[0m'.center(90,'#')%USER_STATUS['username'])
        print('''\033[35;1m
            1. 登陸系統
            2. 添加用戶
            3. 修改密碼
            4. 查詢用戶
            5. 刪除用戶
            6. 提高權限
            7. 解鎖用戶
            8. 退出帳戶
        \033[0m''')
        print('#'*90)
    else:
        print('歡迎來到用戶管理系統'.center(82,'#'))
        print('''
            1. 登陸系統
            2. 註冊用戶
            3. 修改密碼
            4. 查詢用戶
            5. 刪除用戶
            6. 提高權限
            7. 解鎖用戶
            8. 退出程序
        ''')
        print('#'*90)


def main():
    '''
    主函數,調用各個菜單函數
    :return: 暫時沒用到
    '''
    while True:
        print_fun()
        user_input_num = input('請選擇序列').strip()
        if user_input_num == '1':
            if not USER_STATUS['user_status']:  #先判斷用戶沒有登陸才調用實例
                result = login()
                if not result:   #若是result返回爲False,表示認證失敗,退出循環
                    break
            else: #爲True的話,證實用戶已經登陸,不容許重複登陸
                if input('\033[31;1m%s 你已經登陸了,重複登陸你想幹什麼???\033[0m'%USER_STATUS['username']):pass
        elif user_input_num == '2':  #若是輸入2,用戶進入改密碼程序
            result,record = registry_user()  #實例化函數
            registry_flag = False #添加標誌位
            if result:   #registry_user返回兩個函數,若是result爲真, 繼續下面操做
                username = record.split(':')[0]  #切割下,把用戶名切割成一個變量
                with open(USERDB,'a+') as f:     #打開文件以追加模式,把用戶註冊的信息寫入到數據文件中
                    f.seek(0,2)
                    f.write('\n' + record)
                    if input('\033[31;1m[ %s ]註冊成功,按下任意鍵繼續...\033[0m'%username):pass
        elif user_input_num == '3':
            edit_password(USER_STATUS['username'])
        elif user_input_num == '4':
            get_user(USER_STATUS['username'])
        elif user_input_num == '5':
            del_user()
        elif user_input_num == '6':
            result = update_user()
        elif user_input_num == '7':
            unlock_user()
        elif user_input_num == '8':
            if USER_STATUS['user_status']:
                user_input_retry = input('\033[31;1m肯定退出登陸帳戶? y/Y').strip()
                if user_input_retry == 'y' or user_input_retry == 'Y':
                    print('[ %s ]你已經退出程序'%USER_STATUS['username'])
                    USER_STATUS['user_status'] = False
                    USER_STATUS['username'] = False
                    USER_STATUS['user_type'] = False
            else:
                print('bye!')
                break
        else:
            print('\033[31;1m不合法的序列!\033[0m')

if __name__ == '__main__':
    main()
上週做業代碼

定義一個,如a.py文件裏包含:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

def f1():
    print('這是a文件中的打印內容')

而後在b.py中導入使用,import, b.py中的內容:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

import a

a.f1()

執行b.py後結果以下:

這是a文件中的打印內容

4. 導入模塊:

導入模塊的幾種方法:

#最經常使用的
import 模塊名

#導入模塊中的某個功能
from 模塊名 import 功能
from 文件夾.模塊 import 功能

#導入全部功能
from 模塊 import *
#通常都不建議這麼作

#還能夠添加個別名
from 模塊 import 功能 as 別名

一個模塊只能被導入一次,無論你執行了多少次import,這樣就是防止導入模塊被一遍又一遍的執行。

實例,添加一個python解釋器中的tab補全功能。新建一個tab.py文件,內容以下:

#!/usr/bin/env python 
# this content comes from oldboy trainning.
# e_mail:31333741@qq.com
# qqinfo:49000448
# function: python tab config.
# version:1.1 
################################################
# oldboy trainning info.      
# QQ 1986787350 70271111
# site:http://www.etiantian.org
# blog:http://oldboy.blog.51cto.com
# oldboy trainning QQ group: 208160987 45039636
################################################
# python startup file
import sys
import readline
import rlcompleter
import atexit
import os
# tab completion
readline.parse_and_bind('tab: complete')
# history file
histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
try:
    readline.read_history_file(histfile)
except IOError:
    pass
atexit.register(readline.write_history_file, histfile)


del os, histfile, readline, rlcompleter
tab.py

將文件保存到Python的 sys.path中

>>> a = sys.path
>>> for i in a:
...   print(i)
... 

#這些路徑是所有能夠import到的路徑
/usr/lib/python3.4
/usr/lib/python3.4/plat-x86_64-linux-gnu
/usr/lib/python3.4/lib-dynload
/usr/local/lib/python3.4/dist-packages
/usr/lib/python3/dist-packages

#通常狀況下,都將第三方的模塊放到dist-packages目錄下,這塊,咱們也把tab.py放到dist-packages目錄下
import的路徑

通常狀況下,都將第三方的模塊放到dist-packages目錄下,這塊,咱們也把tab.py放到dist-packages目錄下。

然後在python3解釋器下import:

>>> import tab
>>> sys.
Display all 100 possibilities? (y or n)
sys.__class__(              sys._current_frames(        sys.getsizeof(
sys.__delattr__(            sys._debugmallocstats(      sys.getswitchinterval(
sys.__dict__                sys._getframe(              sys.gettrace(
sys.__dir__(                sys._home                   sys.hash_info
sys.__displayhook__(        sys._mercurial              sys.hexversion
sys.__doc__                 sys._xoptions               sys.implementation
sys.__eq__(                 sys.abiflags                sys.int_info
sys.__excepthook__(         sys.api_version             sys.intern(
sys.__format__(             sys.argv                    sys.maxsize
sys.__ge__(                 sys.base_exec_prefix        sys.maxunicode
sys.__getattribute__(       sys.base_prefix             sys.meta_path
sys.__gt__(                 sys.builtin_module_names    sys.modules
sys.__hash__(               sys.byteorder               sys.path
sys.__init__(               sys.call_tracing(           sys.path_hooks
sys.__interactivehook__(    sys.callstats(              sys.path_importer_cache
sys.__le__(                 sys.copyright               sys.platform
sys.__loader__(             sys.displayhook(            sys.prefix
sys.__lt__(                 sys.dont_write_bytecode     sys.ps1
sys.__name__                sys.exc_info(               sys.ps2
sys.__ne__(                 sys.excepthook(             sys.setcheckinterval(
sys.__new__(                sys.exec_prefix             sys.setdlopenflags(
sys.__package__             sys.executable              sys.setprofile(
sys.__reduce__(             sys.exit(                   sys.setrecursionlimit(
sys.__reduce_ex__(          sys.flags                   sys.setswitchinterval(
sys.__repr__(               sys.float_info              sys.settrace(
sys.__setattr__(            sys.float_repr_style        sys.stderr
sys.__sizeof__(             sys.getallocatedblocks(     sys.stdin
sys.__spec__                sys.getcheckinterval(       sys.stdout
sys.__stderr__              sys.getdefaultencoding(     sys.thread_info
sys.__stdin__               sys.getdlopenflags(         sys.version
sys.__stdout__              sys.getfilesystemencoding(  sys.version_info
sys.__str__(                sys.getprofile(             sys.warnoptions
sys.__subclasshook__(       sys.getrecursionlimit(      
sys._clear_type_cache(      sys.getrefcount(            
>>> sys.
須要補齊,按兩下tab就好了

5. sys.path

上面說到sys.path的路徑,若是不在這個環境變量,import是沒法成功的

>>> a = sys.path
#類型是list列表
>>> type(a)
<class 'list'>

好比說若是在添加一個文件夾testfile, 下面添加一個文件test.py

root@test3-ubunut:~# mkdir testfile
root@test3-ubunut:~# touch testfile/test.py
root@test3-ubunut:~# vim testfile/test.py
#文件內容:

#!/usr/bin/env python3

def f1():
    print('這是來自testfile目錄下test模塊的輸出')

然後在家目錄下建立一個a.py文件,導入test.py,看可否成功:

root@test3-ubunut:~# pwd
/home/putao
root@test3-ubunut:~# vim a.py
#文件內容:

#!/usr/bin/env python3

from testfile import test

test.f1()

執行下試試。

root@test3-ubunut:~# python3 a.py 
這是來自testfile目錄下test模塊的輸出

ok,這個能成功,緣由是Python會去當前目錄下找,由於testfile在當前家目錄,因此能找到。好,如今,我把testfile文件夾放到/tmp目錄下去試試:

# mv testfile /tmp/
#更改a.py

#!/usr/bin/env python3

from tmp.testfile import test
import sys

#print(sys.path)
test.f1()

#執行下試試:
root@test3-ubunut:~# python3 a.py 
Traceback (most recent call last):
  File "a.py", line 3, in <module>
    from tmp.testfile import test
ImportError: No module named 'tmp'

#找不到模塊的
View Code

那麼,咱們這麼試下,sys.path中咱們添加一個環境變量值,/tmp

# 更改下a.py文件,先追加/tmp中到列表中,然後在導入testfile
#!/usr/bin/env python3

import sys
sys.path.append('/tmp')

from testfile import test
test.f1()

執行下看結果:

root@test3-ubunut:~# python3 a.py 
這是來自testfile目錄下test模塊的輸出

六.  序列化相關模塊

先理解下序列化和反序列化的感念:

序列化:把Python對象序列化成字符串

反序列化: 把字符串轉換成Python對象

1.json

json模塊的經常使用功能:

dumps 序列化,把Python類型轉換爲字符串,可是Python類型對象中的值若是是字符串的話,必定要用雙引號。由於在Python中單引號和雙引號咱們能夠不加區分的來使用,但在其餘語言中就不是了,因此對象中的字符串必定要用雙引號。

>>> s1 = {"name":"daniel","age":18}
>>> type(s1)
<class 'dict'>
>>> result = json.dumps(s1)
>>> type(result)
<class 'str'>

loads  反序列化, 把字符串轉換成Python類型

>>> result
'{"name": "daniel", "age": 18}'
>>> type(result)
<class 'str'>
>>> result1 = json.loads(result)
>>> type(result1)
<class 'dict'>
>>> result1
{'name': 'daniel', 'age': 18}

dump  序列化,寫入到文件中

>>> s1 = {"name":"daniel","Age":18}
>>> type(s1)
<class 'dict'>

>>> import json
>>> json.dump(s1,open('test.txt','w'))

#看下文件中的內容:
root@test3-ubunut:~# cat test.txt && echo
{"name": "daniel", "Age": 18}

load   反序列化,從文件中讀取

>>> result = json.load(open('test.txt','r')
... )
>>> type(result)
<class 'dict'>
>>> result
{'name': 'daniel', 'Age': 18}

json 支持的python數據類型:

+---------------+-------------------+
| JSON | Python |
+===============+===================+
| object | dict |
+---------------+-------------------+
| array | list |
+---------------+-------------------+
| string | str |
+---------------+-------------------+
| number (int) | int |
+---------------+-------------------+
| number (real) | float |
+---------------+-------------------+
| true | True |
+---------------+-------------------+
| false | False |
+---------------+-------------------+
| null | None |
+---------------+-------------------+

 

一個實例,抓取天津的天氣信息:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

import json
import requests

response = requests.get('http://wthrcdn.etouch.cn/weather_mini?city=天津')
response.encoding = 'utf-8'
result = json.loads(response.text)
print(result)


執行結果以下:
{'data': {'wendu': '22', 'aqi': '68', 'yesterday': {'fl': '微風', 'low': '低溫 20℃', 'fx': '東南風', 'high': '高溫 30℃', 'type': '多雲', 'date': '6日星期一'}, 'ganmao': '相對今天出現了較大幅度降溫,較易發生感冒,體質較弱的朋友請注意適當防禦。', 'city': '天津', 'forecast': [{'fengli': '微風級', 'low': '低溫 19℃', 'high': '高溫 25℃', 'fengxiang': '東北風', 'type': '陣雨', 'date': '7日星期二'}, {'fengli': '微風級', 'low': '低溫 21℃', 'high': '高溫 32℃', 'fengxiang': '南風', 'type': '', 'date': '8日星期三'}, {'fengli': '3-4級', 'low': '低溫 22℃', 'high': '高溫 34℃', 'fengxiang': '南風', 'type': '多雲', 'date': '9日星期四'}, {'fengli': '3-4級', 'low': '低溫 21℃', 'high': '高溫 33℃', 'fengxiang': '東南風', 'type': '雷陣雨', 'date': '10日星期五'}, {'fengli': '3-4級', 'low': '低溫 19℃', 'high': '高溫 28℃', 'fengxiang': '東北風', 'type': '多雲', 'date': '11日星期六'}]}, 'desc': 'OK', 'status': 1000}
View Code

 

2. pickle

pickle模塊使用的數據格式是Python專用的,而且不一樣的版本不向後兼容,其餘語言也不能識別,若是要和其餘語言交互,須要使用json模塊。

主要參數:

dumps  序列化

>>> s1
{'name': 'daniel', 'Age': 18}
>>> type(s1)
<class 'dict'>
>>> result = pickle.dumps(s1)
>>> type(result)
<class 'bytes'>
#格式是pickle獨有的,沒法直接讀取
>>> print(result)
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00danielq\x02X\x03\x00\x00\x00Ageq\x03K\x12u.'
View Code

loads   反序列化

>>> type(result)
<class 'bytes'>
>>> r = pickle.loads(result)
>>> print(r)
{'name': 'daniel', 'Age': 18}
>>> type(r)
<class 'dict'>
View Code

dump  序列化,將對象持久化到文件中

>>> s1
{'name': 'daniel', 'Age': 18}
>>> type(s1)
<class 'dict'>
#寫入模式須要用二進制寫入模式,不然報錯
>>> pickle.dump(s1,open('test.txt','wb'))

#查看文件內容
root@test3-ubunut:~# cat test.txt &&echo
?}q(XnameqXdanielqXAgeqKu.

#是亂碼,沒法直接查看
View Code

load   反序列化,從文件中讀取,並轉換爲原來的Python對象

>>> result = pickle.load(open('test.txt','rb'))
>>> type(result)
<class 'dict'>
>>> result
{'name': 'daniel', 'Age': 18}

#注意,讀取時必須使用二進制讀取模式,加b,不然會拋異常!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/codecs.py", line 319, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
View Code

 

七. time、datetime模塊

1. time模塊

time, python中一種管理時間和日期的模塊,經常使用的操做有以下:

clock()  返回當前處理器時間

>>> import time
>>> time.clock()
0.333191

process_time()  返回處理器時間

>>> time.process_time()
0.336428017

time()  返回當前時間戳,從Unix元年(1970/1/1 0點)開始到此時此刻通過的秒數

>>> time.time()
1465288559.7881007

ctime()  返回當前時間,日期格式:'Tue Jun  7 16:36:24 2016'。也能夠將時間戳轉換爲字符串格式。

>>> time.ctime()
'Tue Jun  7 16:36:24 2016'
>>> time.ctime(time.time())
'Tue Jun  7 16:51:45 2016'

gmtime()  返回struct_time 時間格式的當前時間, UTC時間。 也能夠將時間戳轉換爲struct_time格式

>>> time.gmtime()
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=8, tm_min=36, tm_sec=57, tm_wday=1, tm_yday=159, tm_isdst=0)

>>> a = time.gmtime()
>>> type(a)
<class 'time.struct_time'>
View Code
struct_time即用數組的形式表示,共有9個元素,同一個時間戳的struct_time 會由於時區不一樣而不一樣;

tm_year     年
tm_mon=6  月
tm_mday=7 日
tm_hour=8  時
tm_min=36  分
tm_sec=57   秒
tm_wday=1  周,範圍0-6,0是週一,依次類推
tm_yday=159  今天是今年中第幾天,範圍1-366
tm_isdst=0   是不是夏令時,範圍:-1, 0  1
struct_time

localtime()   返回系統時間,struct_time格式。 可將時間戳轉換爲struct_time格式。

>>> time.localtime()
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=16, tm_min=48, tm_sec=43, tm_wday=1, tm_yday=159, tm_isdst=0)
>>> time.time()
1465289371.3065152
#也能夠將時間戳轉換爲struct_time格式
>>> time.localtime(time.time())
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=16, tm_min=49, tm_sec=36, tm_wday=1, tm_yday=159, tm_isdst=0)
View Code

mktime()  與localtime功能相反,將struct_time 格式的時間轉換爲時間戳。

>>> time.mktime(time.localtime())
1465289600.0

sleep()   用過好屢次了,讓程序休眠, 默認單位秒

>>> time.sleep(1)
>>> time.sleep(0.3)

strftime()  將struct_time格式轉換成指定的字符串格式

>>> time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
'2016-06-07 16:55:35'
>>> time.strftime('%Y/%m/%d %H%M%S',time.localtime())
'2016/06/07 165551'
View Code

strptime()  將字符串轉換成struct格式

>>> type(a)
<class 'str'>
>>> a
'2016/06/07 165646'
>>> time.strptime(a,'%Y/%m/%d %H%M%S')
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=16, tm_min=56, tm_sec=46, tm_wday=1, tm_yday=159, tm_isdst=-1)
View Code

python中日期格式化符號

%y  兩位數年(0-99)
%Y  四位數年(0000-9999)
%m 月,01-12
%d  天,0-31
%H  時, 0-23
%I   時,01-12,十二小時制
%M  分,  0-59
%S  秒, 0-59 

%a  本地簡化星期名稱
%A  本地完整星期名稱
%b  本地簡化月份名稱
%B  本地完整月份名稱
%c   本地相應的日期表示和時間表示
%j   年內的一天(01-366)
%p   本地AM或者PM等價符
%U   一年中的星期數(00-53),星期天爲一星期的開始
%w   星期(0-6)
%W  一年中的星期數(0-53)  星期一爲一星期的開始
%x   本地相應的日期表示
%X   本地相應的時間顯示
%Z   當前時區名字
%%  %自己
>>> time.strftime('%x')
'06/07/16'
>>> time.strftime('%X')
'17:08:44'
>>> time.strftime('%Z')
'CST'
>>> time.strftime('%a')
'Tue'
>>> time.strftime('%A')
'Tuesday'
>>> time.strftime('%b')
'Jun'
>>> time.strftime('%c')
'Tue Jun  7 17:09:02 2016'
>>> time.strftime('%j')
'159'
>>> time.strftime('%p')
'PM'
>>> time.strftime('%U')
'23'
>>> time.strftime('%w')
'2'
>>> time.strftime('%W')
'23'
>>> time.strftime('%A')
'Tuesday'
View Code

2. datetime

datetime模塊也是Python提供用於操做日期和時間的模塊,datetime定義了下面幾個類:

datetime.date   日期類,經常使用的有year、month、day等;

datetime.time   時間類,經常使用的屬性有hour、minute、second、microsecond等;

datetime.datetimt   日期時間類

datetime.tzinfo   與時區相關的

date日期類:

date.max date.min date對象所能表示最大的時間和最小的時間

>>> datetime.date.max
datetime.date(9999, 12, 31)
>>> datetime.date.min
datetime.date(1, 1, 1)
View Code

date.today()  返回當前日期

>>> datetime.date.today()
datetime.date(2016, 6, 7)

date.fromtimestamp()  將時間戳轉換爲日期格式

>>> a = time.time()
>>> datetime.date.fromtimestamp(a)
datetime.date(2016, 6, 7)
View Code

date.replace(year, month, day) 生成一個新的日期對象,用指定的年、月、日替代原有對象中的屬性,原有對象不變。

>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2016, 6, 7, 17, 25, 9, 730194)
>>> tomorrow = now.replace(day = 8)
>>> tomorrow
datetime.datetime(2016, 6, 8, 17, 25, 9, 730194)
View Code

date.timetuple()  返回日期對應的struct_time 對象;

>>> datetime.date.timetuple(now)
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=159, tm_isdst=-1)
View Code

date.weekday()   返回周幾,範圍0-6

>>> datetime.date.weekday(now)
1
View Code

date.isoweekday()  返回周幾,範圍1-7

>>> datetime.date.isoweekday(now)
2
>>> now
datetime.datetime(2016, 6, 7, 17, 25, 9, 730194)
View Code

date.isocalendar()   返回日期元組,包括(year, month,day) 

>>> datetime.date.isocalendar(now)
(2016, 23, 2)
View Code

date.isoformat()  返回格式YYYY-MM-DD的字符串

>>> datetime.date.isoformat(datetime.datetime.now())
'2016-06-07'
View Code

time時間類:

time類表示時間,由小時、分鐘、秒以及毫秒組成。

time.min  time.max time類所能表示最小、最大的值;

>>> datetime.time.min
datetime.time(0, 0)
>>> datetime.time.max
datetime.time(23, 59, 59, 999999)
View Code

time.resolution  時間的最小單位,1微秒

>>> datetime.time.resolution
datetime.timedelta(0, 0, 1)
View Code
time類提供的實例方法和屬性

datetime類

datetime類是date和time類的合併,包括他們倆的全部的信息。

datetime.min  datetimt.max  表示的最小值和最大值

>>> datetime.datetime.min
datetime.datetime(1, 1, 1, 0, 0)
>>> datetime.datetime.max
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
View Code

datetime.resolution  最小單位

>>> datetime.datetime.resolution
datetime.timedelta(0, 0, 1)
View Code

datetime.today()  返回當前本地時間

>>> datetime.datetime.today()
datetime.datetime(2016, 6, 7, 17, 44, 32, 339503)
View Code

datetime.now()  返回當前本地時間

>>> datetime.datetime.now()
datetime.datetime(2016, 6, 7, 17, 45, 21, 563377)
View Code

datetime.utcnow()  返回當前utc時間

>>> datetime.datetime.utcnow()
datetime.datetime(2016, 6, 7, 9, 46, 46, 155147)
View Code

datetime.fromtimestamp(timestamp)  將時間戳轉換爲datetime時間

>>> datetime.datetime.fromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 17, 47, 30, 571110)
View Code

datetime.utcfromtimestamp(timestamp)  將時間戳轉換爲utc時間

>>> datetime.datetime.utcfromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 9, 47, 53, 533692)
View Code

datetime.strptime(date,time)  根據date和time,將字符串轉換成日期格式

>>> datetime.datetime.strptime('2016/06/07 17:50:00','%Y/%m/%d %H:%M:%S')
datetime.datetime(2016, 6, 7, 17, 50)
View Code
# 日後十天
>>> new_date = datetime.datetime.now() + datetime.timedelta(days=10)
>>> new_date
datetime.datetime(2016, 6, 17, 17, 54, 55, 709101)

#往前20天
>>> new_date = datetime.datetime.now() - datetime.timedelta(days=20)
>>> new_date
datetime.datetime(2016, 5, 18, 17, 55, 45, 566495)

#20小時前
>>> new_date = datetime.datetime.now() - datetime.timedelta(hours=20)
>>> new_date
datetime.datetime(2016, 6, 6, 21, 56, 24, 738440)

# 一週前
>>> new_date = datetime.datetime.now() - datetime.timedelta(days=7)
>>> new_date
datetime.datetime(2016, 5, 31, 17, 57, 45, 228619)

# 20秒以後
>>> new_date = datetime.datetime.now() + datetime.timedelta(seconds=20)
>>> new_date
datetime.datetime(2016, 6, 7, 18, 0, 32, 871318)
一些日期操做例子

 

八. logging模塊

關於日期模塊(logging)幾個重要的概念:

  • Logger: 記錄器,應用程序(代碼)直接可使用的接口。
  • Handle: 處理器,將記錄器產生的日誌記錄發送給合適的目的地,主要有:屏幕(StreamHandler)、文件(FileHandler)、syslog(SyslogHandler)等,更多點我
  • Filter:    過濾器,提供良好的粒度控制,決定輸出哪些日誌記錄。
  • Formatter: 格式化工廠,定義輸出日誌記錄的格式。

日誌級別:

  • DEBUG:   調試模式,記錄詳細信息,經常使用於調試程序。 數字值:10
  • INFO:    通常信息,程序運行的正常運行信息。 數字值:20
  • WARN:  警告級別,表示發生一些意外,或者將要發生意外,好比警告磁盤滿了,但程序仍是能工做。數字值:30
  • ERROR:  錯誤級別,比警告更嚴重,程序已經不能執行某些功能了。數字值:40
  • CRITICAL: 嚴重錯誤,比error更嚴重,程序已經不能工做了。 數字值:50

1. Logger記錄器

Logger是一個樹形層級結構,使用接口以前必須建立Logger實例,也就是建立一個記錄器,若是沒有顯式的建立,則默認建立一個root Logger,並應用默認日誌級別爲Warn,處理器Handler(StramHandler),和格式化工程Formatter(默認格式是一個簡單使用程序輸出的格式)。

#建立方法:
import logging
logger = logging.getLogger('MyAPP')
import logging

logging.warning('warning msg')
logging.info('info msg')

#執行程序以後,顯示:
WARNING:root:warning msg

#由於默認的輸出日誌級別是warn
簡單例子

建立Logger後,就可使用下面方法設置日誌級別了,增長處理器Handler。

logger.setLevel(logging.INFO)  #設置日誌級別爲Info,也就是Info級別以上的纔會輸出,Debug不會輸出

logger.addHandler('MyHandler')  #爲Logger增長一個處理器

logger.removeFilter('Myhandler') #爲Logger刪除一個處理器

2. Handler處理器

Handler處理器有好多,咱們這塊只討論StreamHandler(輸出到console),FileHandler(輸出到文件), 更多請參照Python官方介紹

建立Handler:

ch = logging.StreamHandler(stream=None)  
fh = logging.FileHandler('myapp.log',mode='a',encoding='utf-8')

建立完成Handler後,就能夠設置日誌級別,設置格式化Formatter,增長或者刪除Filter了:

ch.setLevel(logging.INFO)   #設置日誌級別
fh.setLevel(logging.WARN)   

ch.addFilter('MyAPP_filter')  #增長一個過濾器,能夠增長多個
fh.addFilter('MyAPP_filter')
import logging
#記錄到文件,並輸出級別爲DEBUG
logging.basicConfig(filename='test.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

#執行程序後,打開test.log文件,內容以下:
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too


#由於輸出日誌級別是debug,因此debug級別以上的信息所有會記錄
記錄到文件例子
import logging
logging.basicConfig(format='%(levelname)s:%(msg)s',level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')

#執行後顯示:
DEBUG:debug message
INFO:info message
WARNING:warn message

#能夠看到沒有root了
改變現實消息格式
import logging
logging.basicConfig(format='%(asctime)s %(levelname)s:%(msg)s',level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')


#執行後顯示:
2016-06-07 22:10:42,186 DEBUG:debug message
2016-06-07 22:10:42,187 INFO:info message
2016-06-07 22:10:42,187 WARNING:warn message

#默認顯示上面所示的日期時間格式,提供給datefmt參數給basicConfig便可,以下:
import logging
logging.basicConfig(format='%(asctime)s %(levelname)s:%(msg)s',level=logging.DEBUG,datefmt='%Y/%m/%d %H:%M:%S')
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')


#執行後返回:
2016/06/07 22:14:16 DEBUG:debug message
2016/06/07 22:14:16 INFO:info message
2016/06/07 22:14:16 WARNING:warn message
在消息中顯示日期/時間

3. Filter 過濾器

Handler和Logger可使用Filter來完成比Level更復雜的過濾。filter基類只容許Logger層次如下的事件。

Filter決定哪些記錄須要發給Handler,能夠用在Handler和Logger上。

實例,將日誌打印到分別打印到屏幕和文件中:

import logging

#先定義一個Logger
logger = logging.getLogger('MyApp')
logger.setLevel(logging.DEBUG)

#建立console,並設定日誌級別爲debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

#建立文件Handler
fh = logging.FileHandler('access.log')
fh.setLevel(logging.INFO)

#建立格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#然後添加formatter到ch和fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)

#添加Handler到Logger中
logger.addHandler(ch)
logger.addHandler(fh)

#應用日誌
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
分別打印日誌到屏幕和文件
2016-06-07 22:39:29,107 - MyApp - DEBUG - debug message
2016-06-07 22:39:29,108 - MyApp - INFO - info message
2016-06-07 22:39:29,108 - MyApp - WARNING - warn message
2016-06-07 22:39:29,108 - MyApp - ERROR - error message
2016-06-07 22:39:29,108 - MyApp - CRITICAL - critical message
執行程序屏幕輸出內容
2016-06-07 22:39:29,108 - MyApp - INFO - info message
2016-06-07 22:39:29,108 - MyApp - WARNING - warn message
2016-06-07 22:39:29,108 - MyApp - ERROR - error message
2016-06-07 22:39:29,108 - MyApp - CRITICAL - critical message
access.log文件內容

4. Formatter

使用Formatter對象設置日誌信息最後的規則,結構和內容,默認的時間格式是%Y-%m-%d %H:%M:%S。

formatter = logging.Formatter(fmt=None, datefmt=None)

fmt是消息格式化字符串,datefmt是日期字符串。若是指明fmt,將使用‘%(message)s'

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

5. Logging模塊處理流程(圖片來自官網)

  • 首先判斷日誌等級是否大於Logger等級,大於往下,不然結束;
  • 生成日誌。首先判斷是否異常,是,則添加異常信息。然後根據日誌級別作通常格式化;
  • 使用註冊到Logger中的Filter來過濾。若是有多個過濾器則依次過濾,只要有一個過濾器返回爲假,則過濾結束,而且將該日誌丟棄。再也不處理,而處理流程也至此結束。不然,繼續往下;
  • 在當前Logger中查找Handler,若是找不到,則往上到Logger的父Logger中找;若是找到一個或者多個Handler,則依次按Handler來處理。在每一個Handler處理過程當中,會首先判斷日誌等級是否大於該Handler的等級,若是大於,則往下執行,不然,處理流程結束。
  • 執行handler中的filter方法,該方法會依次執行註冊該Handler中的Filter。若是有一個Filter判斷該日誌信息爲假,則後面的全部Filter都再也不繼續執行,而直接將該日誌信息丟棄,處理流程結束。
  • 使用Formatter格式化最終的日誌結果。
  • 輸出日誌信息到目標(由Handler肯定輸入目標)。

  注: 以上文字信息摘抄自簡書,感謝簡書做者提供優質的教程。此處內容稍有改動,特此聲明。

6. 日誌格式

%(asctime)s       日誌時間
%(created)f        打印時間戳
%(filename)s      返回文件名稱,文件全名
%(funcName)s    打印當前函數
%(levelno)s        打印級別號碼
%(levenname)s   日誌級別名稱
%(lineno)d          打印調用行號
%(module)s        打印模塊
%(msecs)d          微秒
%(message)s       日誌信息
%(name)s Logger  名稱
%(pathname)s      日誌文件的絕對路徑
%(process)d          進程ID
%(processName)s  進程名
%(relativeCreated)d 運行時長
%(thread)d           線程ID
%(threadName)s    線程名

7. 日誌配置

配置方式

  • 顯式建立記錄器Logger、處理器Handler和格式化Formatter,並進行設置;
  • 經過簡單方式進行配置,使用 basicConfig() 函數來配置;
  • 經過配置文件進行配置,使用 fileConfig() 函數來讀取配置文件;
  • 經過配置字典進行配置,使用 dictConfig() 函數讀取配置信息;
  • 經過網絡進行配置,使用 listen() 函數來進行網絡配置。

basicConfig關鍵字參數

filename 建立一個Filehandler,使用文件名filemode 若是指明瞭文件名,應該指定打開文件模式,若是不指定,默認爲a追加模式。format handler使用指明的格式化字符串datefmt 使用指明的日期時間格式。level 日誌級別stream 使用指明的流來初始化StreamHandler。這個參數與filename不兼容。若是兩個都有,stream被忽略。

相關文章
相關標籤/搜索