今日目錄: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)
或者本身定義一個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'"
%c 單個字符, 將數字轉換爲unicode對應的值(還記得上個文章說的ASCII表麼?),10進制範圍爲0 <=i <= 1114111
>>> print('%c' %a) A >>> a = 66 >>> print('%c' %a) B >>> a = 89 >>> print('%c' %a) Y
%d 十進制整數
>>> a = 20 >>> print('%d'%a) 20 >>> print('%d'%a) 20 >>> a = 25 >>> print('%d'%a) 25
%i 十進制整數
>>> print('%i'%a) 25 >>> a = 89 >>> print('%i'%a) 89 >>> a = 8901 >>> print('%i'%a) 8901
%o 八進制整數, 將整數轉換爲8進製表示
>>> a = 8901 >>> print('%o'%a) 21305 >>> a = 10 >>> print('%o'%a) 12
%x 十六進制整數, 將整數轉換爲16進製表示
>>> a = 10 >>> print('%x'%a) a >>> a = 15 >>> print('%x'%a) f >>> a = 12330 >>> print('%x'%a) 302a
%e 指數(小e)
>>> a = 12330 >>> print('%e'%a) 1.233000e+04
%E 指數(大寫E)
>>> a = 12330 >>> print('%E'%a) 1.233000E+04
%f 浮點數,默認顯示到小數點後6位
>>> a = 3.1415926 >>> print('%f'%a) 3.141593
%F 浮點數,默認顯示到小數點後6位
>>> a = 3.1415926 >>> print('%F'%a) 3.141593
%g 指數e或浮點數(根據長度顯示)
>>> a = 1300882 >>> print('%g'%a) 1.30088e+06
%G 指數E或浮點數(根據長度顯示)
>>> a = 1300882 >>> print('%G'%a) 1.30088E+06
注意,要想在%方式格式化字符串中輸出%,必需要使用%%來轉譯!
>>> 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
2. format方式
format格式是比較新的字符串格式化方式,大有替代%s的方式,不過目前官方沒有明確表示要替換掉%s. 相比%s方式,format更增強大;
格式:
[[fill]align][sign][#][0][width][,][.precision][type]
>>> print('{:#^30} balabala'.format('tianchong')) ##########tianchong########### balabala
< 左對齊
> 右對齊
= 右對齊, 將符號放在填充字符的左側,並且只是對數字類型生效,若是使用字符串會報錯!
^ 居中顯示
#居中顯示,並以#號填充 >>> 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
+ 正加正、負加負
- 正不變、負加負
空格 正好空格, 負加負
#+, 正加正,負加負 >>> print('{:#^+30}'.format(600)) #############+600############# #-, 正不變,負加負 >>> print('{:#^-30}'.format(600)) #############600############## #空格, 正加空格,負加負 >>> print('{:#^ 30}'.format(600)) ############# 600#############
>>> 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
>>> print('{:,d}'.format(203301010293)) 203,301,010,293
#通常都是配合fill、align和sign一塊兒使用 >>> print('{:10,d}'.format(203301010293)) 203,301,010,293
#默認顯示小數點位數後面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
字符串類型的參數:
字符型:
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
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
使用這些模塊是,先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哈,不要鑽牛角尖哈)
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...
先下載源碼到目錄,然後解壓:
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__']
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
將文件保存到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目錄下
通常狀況下,都將第三方的模塊放到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.
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' #找不到模塊的
那麼,咱們這麼試下,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}
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.'
loads 反序列化
>>> type(result) <class 'bytes'> >>> r = pickle.loads(result) >>> print(r) {'name': 'daniel', 'Age': 18} >>> type(r) <class 'dict'>
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. #是亂碼,沒法直接查看
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
七. 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'>
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
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)
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'
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)
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'
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)
date.today() 返回當前日期
>>> datetime.date.today()
datetime.date(2016, 6, 7)
date.fromtimestamp() 將時間戳轉換爲日期格式
>>> a = time.time() >>> datetime.date.fromtimestamp(a) datetime.date(2016, 6, 7)
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)
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)
date.weekday() 返回周幾,範圍0-6
>>> datetime.date.weekday(now)
1
date.isoweekday() 返回周幾,範圍1-7
>>> datetime.date.isoweekday(now) 2 >>> now datetime.datetime(2016, 6, 7, 17, 25, 9, 730194)
date.isocalendar() 返回日期元組,包括(year, month,day)
>>> datetime.date.isocalendar(now)
(2016, 23, 2)
date.isoformat() 返回格式YYYY-MM-DD的字符串
>>> datetime.date.isoformat(datetime.datetime.now()) '2016-06-07'
time時間類:
time類表示時間,由小時、分鐘、秒以及毫秒組成。
time.min time.max time類所能表示最小、最大的值;
>>> datetime.time.min datetime.time(0, 0) >>> datetime.time.max datetime.time(23, 59, 59, 999999)
time.resolution 時間的最小單位,1微秒
>>> datetime.time.resolution
datetime.timedelta(0, 0, 1)
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)
datetime.resolution 最小單位
>>> datetime.datetime.resolution
datetime.timedelta(0, 0, 1)
datetime.today() 返回當前本地時間
>>> datetime.datetime.today()
datetime.datetime(2016, 6, 7, 17, 44, 32, 339503)
datetime.now() 返回當前本地時間
>>> datetime.datetime.now()
datetime.datetime(2016, 6, 7, 17, 45, 21, 563377)
datetime.utcnow() 返回當前utc時間
>>> datetime.datetime.utcnow()
datetime.datetime(2016, 6, 7, 9, 46, 46, 155147)
datetime.fromtimestamp(timestamp) 將時間戳轉換爲datetime時間
>>> datetime.datetime.fromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 17, 47, 30, 571110)
datetime.utcfromtimestamp(timestamp) 將時間戳轉換爲utc時間
>>> datetime.datetime.utcfromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 9, 47, 53, 533692)
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)
# 日後十天 >>> 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)幾個重要的概念:
日誌級別:
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
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模塊處理流程(圖片來自官網)
注: 以上文字信息摘抄自簡書,感謝簡書做者提供優質的教程。此處內容稍有改動,特此聲明。
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. 日誌配置
配置方式
basicConfig關鍵字參數
filename 建立一個Filehandler,使用文件名filemode 若是指明瞭文件名,應該指定打開文件模式,若是不指定,默認爲a追加模式。format handler使用指明的格式化字符串datefmt 使用指明的日期時間格式。level 日誌級別stream 使用指明的流來初始化StreamHandler。這個參數與filename不兼容。若是兩個都有,stream被忽略。