Python學習記錄day5
1.多層裝飾器
多層裝飾器的原理是,裝飾器裝飾函數後,其實也是一個函數,這樣又可以被裝飾器裝飾。 編譯是從下至上進行的,執行時是從上至下進行。
#!/usr/bin/env python
# _*_coding:utf-8_*_
‘‘‘
* Created on 2016/11/29 20:38.
* @author: Chinge_Yang.
‘‘‘
USER_INFO = {}
# USER_INFO[‘is_login‘] = True
# USER_INFO[‘user_type‘] = 2
def check_login(func):
def inner(*args, **kwargs):
if USER_INFO.get("is_login", None):
ret = func(*args, **kwargs)
return ret
else:
print("請登錄")
return inner
def check_admin(func):
def inner(*args, **kwargs):
if USER_INFO.get(‘user_type‘, None) == 2:
ret = func(*args, **kwargs)
return ret
else:
print(‘無權限查看‘)
return inner
@check_login
@check_admin
def index():
"""
管理員的功能
:return:
"""
print(‘Index‘)
@check_login
def home():
"""
普通用戶功能
:return:
"""
print("home")
def login():
user = input("請輸入用戶名:")
if user == "admin":
USER_INFO["is_login"] = True
USER_INFO["user_type"] = 2
else:
USER_INFO["is_login"] = True
USER_INFO["user_type"] = 1
def main():
while True:
inp = input("1.登錄;2.查看信息;3.超級管理員管理\n >>>")
if inp == "1":
login()
elif inp == "2":
home()
elif inp == "3":
index()
elif inp == "q":
exit()
main()
2.字符串格式化
Python的字符串格式化有兩種方式: 百分號方式、format方式
百分號的方式相對來說比較老,而format方式則是比較先進的方式,企圖替換古老的方式,目前兩者並存。[PEP-3101]
This PEP proposes a new system for built-in string formatting operations, intended as a replacement for the existing ‘%‘ string formatting operator.
1.百分號方式
%[(name)][flags][width].[precision]typecode
- (name) 可選,用於選擇指定的key
- flags 可選,可供選擇的值有:
+ 右對齊;正數前加正好,負數前加負號;
- 左對齊;正數前無符號,負數前加負號;
空格 右對齊;正數前加空格,負數前加負號;
0 右對齊;正數前無符號,負數前加負號;用0填充空白處 - width 可選,占有寬度
- .precision 可選,小數點後保留的位數
- typecode 必選
s,獲取傳入對象的__str__方法的返回值,並將其格式化到指定位置
r,獲取傳入對象的__repr__方法的返回值,並將其格式化到指定位置
c,整數:將數字轉換成其unicode對應的值,10進制範圍為 0 <= i <= 1114111(py27則只支持0-255);字符:將字符添加到指定位置
o,將整數轉換成 八 進制表示,並將其格式化到指定位置
x,將整數轉換成十六進制表示,並將其格式化到指定位置
d,將整數、浮點數轉換成 十 進制表示,並將其格式化到指定位置
E,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(大寫E)
f, 將整數、浮點數轉換成浮點數表示,並將其格式化到指定位置(默認保留小數點後6位)
F,同上
g,自動調整將整數、浮點數轉換成浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是e;)
G,自動調整將整數、浮點數轉換成浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是E;)
%,當字符串中存在格式化標誌時,需要用 %%表示一個百分號
註:Python中百分號格式化是不存在自動將整數轉換成二進制表示的方式
常用格式化:
tpl = "i am %s" % "ygqygq2"
tpl = "i am %s age %d" % ("ygqygq2", 27)
tpl = "i am %(name)s age %(age)d" % {"name": "ygqgyq2", "age": 27}
tpl = "percent %.2f" % 99.97623
tpl = "i am %(pp).2f" % {"pp": 123.425556, }
tpl = "i am %.2f %%" % {"pp": 123.425556, }
2.Format方式
[[fill]align][sign][#][0][width][,][.precision][type]
- fill 【可選】空白處填充的字符
- align 【可選】對齊方式(需配合width使用)
<,內容左對齊
>,內容右對齊(默認)
=,內容右對齊,將符號放置在填充字符的左側,且只對數字類型有效。即使:符號+填充物+數字
^,內容居中
sign 【可選】有無符號數字
+,正號加正,負號加負;
-,正號不變,負號加負;
空格 ,正號空格,負號加負;
# 【可選】對於二進制、八進制、十六進制,如果加上#,會顯示0b/0o/0x,否則不顯示
, 【可選】為數字添加分隔符,如:1,000,000
width 【可選】格式化位所占寬度
.precision 【可選】小數位保留精度
type 【可選】格式化類型
傳入” 字符串類型 “的參數
s,格式化字符串類型數據
空白,未指定類型,則默認是None,同s
傳入“ 整數類型 ”的參數
b,將10進制整數自動轉換成2進制表示然後格式化
c,將10進制整數自動轉換為其對應的unicode字符
d,十進制整數
o,將10進制整數自動轉換成8進制表示然後格式化;
x,將10進制整數自動轉換成16進制表示然後格式化(小寫x)
X,將10進制整數自動轉換成16進制表示然後格式化(大寫X)
傳入“ 浮點型或小數類型 ”的參數
e, 轉換為科學計數法(小寫e)表示,然後格式化;
E, 轉換為科學計數法(大寫E)表示,然後格式化;
f , 轉換為浮點型(默認小數點後保留6位)表示,然後格式化;
F, 轉換為浮點型(默認小數點後保留6位)表示,然後格式化;
g, 自動在e和f中切換
G, 自動在E和F中切換
%,顯示百分比(默認顯示小數點後6位)
常用格式化:
tpl = "i am {}, age {}, {}".format("seven", 18, ‘alex‘)
tpl = "i am {}, age {}, {}".format(*["seven", 18, ‘alex‘])
tpl = "i am {0}, age {1}, really {0}".format("seven", 18)
tpl = "i am {0}, age {1}, really {0}".format(*["seven", 18])
tpl = "i am {name}, age {age}, really {name}".format(name="seven", age=18)
tpl = "i am {name}, age {age}, really {name}".format(**{"name": "seven", "age": 18})
tpl = "i am {0[0]}, age {0[1]}, really {0[2]}".format([1, 2, 3], [11, 22, 33])
tpl = "i am {:s}, age {:d}, money {:f}".format("seven", 18, 88888.1)
tpl = "i am {:s}, age {:d}".format(*["seven", 18])
tpl = "i am {name:s}, age {age:d}".format(name="seven", age=18)
tpl = "i am {name:s}, age {age:d}".format(**{"name": "seven", "age": 18})
tpl = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2)
tpl = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2)
tpl = "numbers: {0:b},{0:o},{0:d},{0:x},{0:X}, {0:%}".format(15)
tpl = "numbers: {num:b},{num:o},{num:d},{num:x},{num:X}, {num:%}".format(num=15)
更多格式化操作:https://docs.python.org/3/library/string.html
3.叠代器和生成器
1.叠代器
叠代器是訪問集合元素的一種方式。叠代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。叠代器只能往前不會後退,不過這也沒什麽,因為人們很少在叠代途中往後退。另外,叠代器的一大優點是不要求事先準備好整個叠代過程中所有的元素。叠代器僅僅在叠代到某個元素時才計算該元素,而在這之前或之後,元素可以不存在或者被銷毀。這個特點使得它特別適合用於遍歷一些巨大的或是無限的集合,比如幾個G的文件
特點:
訪問者不需要關心叠代器內部的結構,僅需通過next()方法不斷去取下一個內容
不能隨機訪問集合中的某個值 ,只能從頭到尾依次訪問
訪問到一半時不能往回退
便於循環比較大的數據集合,節省內存
>>> a = iter([1,2,3,4,5])
>>> a
<list_iterator object at 0x101402630>
>>> a.__next__()
1
>>> a.__next__()
2
>>> a.__next__()
3
>>> a.__next__()
4
>>> a.__next__()
5
>>> a.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
2.生成器
一個函數調用時返回一個叠代器,那這個函數就叫做生成器(generator);如果函數中包含yield語法,那這個函數就會變成生成器;
def func():
print(11)
yield 1
print(22)
yield 2
print(33)
yield 3
print(44)
yield 4
上述代碼中:func是函數稱為生成器,當執行此函數func()時會得到一個叠代器。
>>> temp = func()
>>> temp.__next__()
11
1
>>> temp.__next__()
22
2
>>> temp.__next__()
33
3
>>> temp.__next__()
44
4
>>> temp.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
4.函數遞歸
在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
def func(n):
n += 1
if n >= 4:
return "end"
return func(n)
res = func(1)
print(res)
遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。
使用遞歸函數需要註意防止棧溢出。
詳細請看廖雪峰的官方網站中遞歸函數
5.模塊
為了編寫可維護的代碼,我們把很多函數分組,分別放到不同的文件裏,這樣,每個文件包含的代碼就相對較少,很多編程語言都采用這種組織代碼的方式。在Python中,一個.py文件就稱之為一個模塊(Module)。
模塊分為三種:
* 自定義模塊
* 第三方模塊
* 內置模塊
1.使用模塊有什麽好處?
1. 最大的好處是大大提高了代碼的可維護性。
2. 使用模塊還可以避免函數名和變量名沖突。
2.導入模塊
單模塊:import
嵌套在文件夾下:
from xxx import xxx
from xxx import as ooo
3.第三方模塊
pip3安裝(pip安裝)
pip3 install requests
源碼安裝
curl -OL https://github.com/kennethreitz/requests/tarball/master
解壓後,進入目錄
python setup.py install
6.序列化
Python中用於序列化的兩個模塊
- json 用於【字符串】和【python基本數據類型】間進行轉換
- pickle 用於【python特有的類型】和【python基本數據類型】間進行轉換
Json模塊提供了四個功能:dumps、dump、loads、load
pickle模塊提供了四個功能:dumps、dump、loads、load
json相關用法:
import json
dict = {‘k1‘: ‘v1‘}
print(dict, type(dict))
# 將python基本數據類型轉換成字符串形式
res = json.dumps(dict)
print(res, type(res))
{‘k1‘: ‘v1‘} <class ‘dict‘>
{"k1": "v1"} <class ‘str‘>
import json
# 將python字符串形式轉換成基本數據類型
s1 = ‘{"k1": 123}‘
dict = json.loads(s1) # 反序列化時,一定要使用 ""
print(dict, type(dict))
{‘k1‘: 123} <class ‘dict‘>
import json
li = [11,22,33]
json.dump(li,open(‘test.txt‘,‘w‘))
li = json.load(open(‘test.txt‘,‘r‘))
print(type(li),li)
pickle相關用法:
import pickle
li = [11,22,33]
r = pickle.dumps(li)
print(r)
res = pickle.loads(r)
print(res)
b‘\x80\x03]q\x00(K\x0bK\x16K!e.‘
[11, 22, 33]
li = [11,22,33]
pickle.dump(li, open(‘test4.txt‘, ‘wb‘))
res = pickle.load(open(‘test4.txt‘, ‘rb‘))
print(res)
[11, 22, 33]
json和pickle對比:
1. json更適合跨語言,字符串,基本數據類型
2. pickle更適合python所有類型的序列化操作
7.time & datetime模塊
#_*_coding:utf-8_*_
__author__ = ‘Alex Li‘
import time
# print(time.clock()) #返回處理器時間,3.3開始已廢棄 , 改成了time.process_time()測量處理器運算時間,不包括sleep時間,不穩定,mac上測不出來
# print(time.altzone) #返回與utc時間的時間差,以秒計算# print(time.asctime()) #返回時間格式"Fri Aug 19 11:14:16 2016",
# print(time.localtime()) #返回本地時間 的struct time對象格式
# print(time.gmtime(time.time()-800000)) #返回utc時間的struc時間對象格式
# print(time.asctime(time.localtime())) #返回時間格式"Fri Aug 19 11:14:16 2016",
#print(time.ctime()) #返回Fri Aug 19 12:38:29 2016 格式, 同上
# 日期字符串 轉成 時間戳
# string_2_struct = time.strptime("2016/05/22","%Y/%m/%d") #將 日期字符串 轉成 struct時間對象格式
# print(string_2_struct)
# #
# struct_2_stamp = time.mktime(string_2_struct) #將struct時間對象轉成時間戳
# print(struct_2_stamp)
#將時間戳轉為字符串格式
# print(time.gmtime(time.time()-86640)) #將utc時間戳轉換成struct_time格式
# print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #將utc struct_time格式轉成指定的字符串格式
#時間加減
import datetime
# print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925
#print(datetime.date.fromtimestamp(time.time()) ) # 時間戳直接轉成日期格式 2016-08-19
# print(datetime.datetime.now() )
# print(datetime.datetime.now() + datetime.timedelta(3)) #當前時間+3天
# print(datetime.datetime.now() + datetime.timedelta(-3)) #當前時間-3天
# print(datetime.datetime.now() + datetime.timedelta(hours=3)) #當前時間+3小時
# print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #當前時間+30分
#
# c_time = datetime.datetime.now()
# print(c_time.replace(minute=3,hour=2)) #時間替換
8.logging模塊
很多程序都有記錄日誌的需求,並且日誌中包含的信息即有正常的程序訪問日誌,還可能有錯誤、警告等信息輸出,python的logging模塊提供了標準的日誌接口,你可以通過它存儲各種格式的日誌,logging的日誌可以分為 debug(), info(), warning(), error() and critical() 5個級別,下面我們看一下怎麽用。
最簡單用法
import logging
logging.warning("user [alex] attempted wrong password more than 3 times")
logging.critical("server is down")
WARNING:root:user [alex] attempted wrong password more than 3 times
CRITICAL:root:server is down
看一下這幾個日誌級別分別代表什麽意思
Level | When it’s used |
---|---|
DEBUG | Detailed information, typically of interest only when diagnosing problems. |
INFO | Confirmation that things are working as expected. |
WARNING | An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected. |
ERROR | Due to a more serious problem, the software has not been able to perform some function. |
CRITICAL | A serious error, indicating that the program itself may be unable to continue running. |
如果想把日誌寫到文件裏,也很簡單
import logging
logging.basicConfig(filename=‘example.log‘,level=logging.INFO)
logging.debug(‘This message should go to the log file‘)
logging.info(‘So should this‘)
logging.warning(‘And this, too‘)
其中下面這句中的level=loggin.INFO意思是,把日誌紀錄級別設置為INFO,也就是說,只有比日誌是INFO或比INFO級別更高的日誌才會被紀錄到文件裏,在這個例子, 第一條日誌是不會被紀錄的,如果希望紀錄debug的日誌,那把日誌級別改成DEBUG就行了。
logging.basicConfig(filename=‘example.log‘,level=logging.INFO)
感覺上面的日誌格式忘記加上時間啦,日誌不知道時間怎麽行呢,下面就來加上!
import logging
logging.basicConfig(format=‘%(asctime)s %(message)s‘, datefmt=‘%m/%d/%Y %I:%M:%S %p‘)
logging.warning(‘is when this event was logged.‘)
12/12/2010 11:46:36 AM is when this event was logged.
日誌格式
%(name)s | Logger的名字 |
---|---|
%(levelno)s | 數字形式的日誌級別 |
%(levelname)s | 文本形式的日誌級別 |
%(pathname)s | 調用日誌輸出函數的模塊的完整路徑名,可能沒有 |
%(filename)s | 調用日誌輸出函數的模塊的文件名 |
%(module)s | 調用日誌輸出函數的模塊名 |
%(funcName)s | 調用日誌輸出函數的函數名 |
%(lineno)d | 調用日誌輸出函數的語句所在的代碼行 |
%(created)f | 當前時間,用UNIX標準的表示時間的浮 點數表示 |
%(relativeCreated)d | 輸出日誌信息時的,自Logger創建以 來的毫秒數 |
%(asctime)s | 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒 |
%(thread)d | 線程ID。可能沒有 |
%(threadName)s | 線程名。可能沒有 |
%(process)d | 進程ID。可能沒有 |
%(message)s | 用戶輸出的消息 |
如果想同時把log打印在屏幕和文件日誌裏,就需要了解一點復雜的知識 了
Python 使用logging模塊記錄日誌涉及四個主要類,使用官方文檔中的概括最為合適:
1. logger提供了應用程序可以直接使用的接口;
2. handler將(logger創建的)日誌記錄發送到合適的目的輸出;
3. filter提供了細度設備來決定輸出哪條日誌記錄;
4. formatter決定日誌記錄的最終輸出格式。
logger
每個程序在輸出信息之前都要獲得一個Logger。Logger通常對應了程序的模塊名,比如聊天工具的圖形界面模塊可以這樣獲得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模塊可以這樣:
LOG=logging.getLogger(”chat.kernel”)
Logger.setLevel(lel):指定最低的日誌級別,低於lel的級別將被忽略。debug是最低的內置級別,critical為最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或刪除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以設置的日誌級別
handler
handler對象負責發送相關的信息到指定目的地。Python的日誌系統有多種Handler可以使用。有些Handler可以把信息輸出到控制臺,有些Logger可以把信息輸出到文件,還有些 Handler可以把信息發送到網絡上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler()方法添加多個多handler
Handler.setLevel(lel):指定被處理的信息級別,低於lel級別的信息將被忽略
Handler.setFormatter():給這個handler選擇一個格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象
每個Logger可以附加多個Handler。接下來我們就來介紹一些常用的Handler:
1) logging.StreamHandler
使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息。它的構造函數是:
StreamHandler([strm])
其中strm參數是一個文件對象。默認是sys.stderr
2) logging.FileHandler
和StreamHandler類似,用於向一個文件輸出日誌信息。不過FileHandler會幫你打開這個文件。它的構造函數是:
FileHandler(filename[,mode])
filename是文件名,必須指定一個文件名。
mode是文件的打開方式。參見Python內置函數open()的用法。默認是’a‘,即添加到文件末尾。
3) logging.handlers.RotatingFileHandler
這個Handler類似於上面的FileHandler,但是它可以管理文件大小。當文件達到一定大小之後,它會自動將當前日誌文件改名,然後創建 一個新的同名日誌文件繼續輸出。比如日誌文件是chat.log。當chat.log達到指定的大小之後,RotatingFileHandler自動把 文件改名為chat.log.1。不過,如果chat.log.1已經存在,會先把chat.log.1重命名為chat.log.2。。。最後重新創建 chat.log,繼續輸出日誌信息。它的構造函數是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個參數和FileHandler一樣。
maxBytes用於指定日誌文件的最大文件大小。如果maxBytes為0,意味著日誌文件可以無限大,這時上面描述的重命名過程就不會發生。
backupCount用於指定保留的備份文件的個數。比如,如果指定為2,當上面描述的重命名過程發生時,原有的chat.log.2並不會被更名,而是被刪除。
4) logging.handlers.TimedRotatingFileHandler
這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時重新創建日誌文件,而是間隔一定時間就 自動創建新的日誌文件。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數字,而是當前時間。它的構造函數是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數和backupCount參數和RotatingFileHandler具有相同的意義。
interval是時間間隔。
when參數是一個字符串。表示時間間隔的單位,不區分大小寫。它有以下取值:
S 秒
M 分
H 小時
D 天
W 每星期(interval==0時代表星期一)
midnight 每天淩晨
import logging
#create logger
logger = logging.getLogger(‘TEST-LOG‘)
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create file handler and set level to warning
fh = logging.FileHandler("access.log")
fh.setLevel(logging.WARNING)
# create formatter
formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s‘)
# add formatter to ch and fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# add ch and fh to logger
logger.addHandler(ch)
logger.addHandler(fh)
# ‘application‘ code
logger.debug(‘debug message‘)
logger.info(‘info message‘)
logger.warn(‘warn message‘)
logger.error(‘error message‘)
logger.critical(‘critical message‘)
import logging
from logging import handlers
logger = logging.getLogger(__name__)
log_file = "timelog.log"
#fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)
formatter = logging.Formatter(‘%(asctime)s %(module)s:%(lineno)d %(message)s‘)
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.warning("test1")
logger.warning("test12")
logger.warning("test13")
logger.warning("test14")
Python學習記錄day5