第一章 python基礎
初識
-
cpu 內存 硬盤 操做系統python
cpu:計算機的運算和計算中心,至關於人類大腦。
內存:暫時存儲數據,臨時加載數據應用程序,運行速度快,高鐵,斷電即消失,造價很高。
硬盤:磁盤,長期存儲數據。
操做系統:一個軟件,鏈接計算機的硬件與全部軟件之間的一個軟件。
git -
python的編程語言分類web
-
編譯型:將代碼一次性所有編譯成二進制,而後再執行。優勢:執行效率高。缺點:開發效率低,不能跨平臺。表明語言:C面試
-
解釋型:逐行解釋成二進制,逐行運行。優勢:開發效率高,能夠跨平臺。缺點:執行效率低。表明語言:python。正則表達式
-
-
python的種類算法
- Cpython:官方推薦解釋器。能夠轉化成C語言能識別的字節碼。
- Jpython: 能夠轉化成Java語言能識別的字節碼。
- Ironpython:能夠轉化成.net語言能識別的字節碼
- pypy: 動態編譯。
......
-
變量:只是指向某種數據數據庫
-
常量:python中沒有真正的常量,爲了應和其餘語言的口味,所有大寫的變量稱之爲常量。編程
-
while/for 的else:若是循環沒有被break終止則else語句會被執行,若是while被break終止,則不執行else語句。json
-
運算符:算數運算符+、-等;比較運算符>、==等;賦值運算符=、+=等;邏輯運算符not、and、or;成員運算符in、not in。
and or not
windows- 在沒有()的狀況下,優先級:not > and > or,同一優先級從左至右依次計算
-
三元運算符:簡單的if-else語句能夠簡化爲三元運算符。如判斷a,b的大小
c = a if a>b else b
-
編寫代碼原則:開放封閉原則,代碼對於將來的一些拓展必定是要開放的(接口)能夠添加一些功能。
開放封閉原則:
開放:對源碼的拓展是開放的。
封閉:對源碼的修改是封閉的。
*的魔術用法:
*的聚合:用於萬能形參,在函數的定義時, *將全部的位置實參聚合成一個元祖,將這個元祖賦值給args。**將全部的關鍵字參數聚合成一個一個字典,將這個字典賦值給kargs。
*的打散,在函數的調用時*打散的是迭代對象,必須用在可迭代對象上(str,list等)**打散的是字典。
def func(*args): print(args) funs([1,2,3],[4,5,6]) #([1, 2, 3], [4, 5, 6]) funs(*[1,2,3],*[4,5,6]) #*的打散,等同於funs(1, 2, 3, 4, 5, 6) #(1, 2, 3, 4, 5, 6) def func(*args,**kargs): print(args) print(kargs) funs({'百度網盤羣': '980173694'}) #({'百度網盤羣': '980173694'}) {} funs(**{'百度網盤羣': '980173694'})#**的打散,等同於funs(百度網盤羣='980173694') #() {'number': '980173694', 'massage': '歡迎加入百度網盤羣'}
格式化輸出(3.6版本以後)
基礎表達:
name = 'python' age = '18' msg = f'我叫{name},今年{age}' #在引號前添加一個字符f
能夠加表達式:
count = 2 print(f'最終結果:{count**2}') name = 'python' print(f'個人名字是{name.upper()}') dic = {'name':'python','age':'18'} msg = f'我叫{dic["name"]},今年{dic["age"]}' #注意雙引號與單引號的使 list = ['python','18'] msg = f'我叫{list[0]},今年{list[1]}'
能夠結合函數:
def sum1(a,b): return a+b print(f'最終結果是{sum1(1,2)}')
優勢:1.結構更加優化。2.能夠結合表達式和函數使用。3.效率更高
編碼
計算機存儲文件,存儲數據,以及經過網絡發送出去,儲存發送數據底層都是0與1的二進制。
密碼本:0101011001 二進制與文字之間的關係
ASCII碼:只包含英文字母,數字,特殊字符。共8位,但只用了7位,能夠表示128(2**7)個不一樣的字符。ASCII碼預留了一位,第8位爲0。
字節:8bit(位) = 1byte(字節)
中國:gbk(最多能表示2**16箇中文),只包含英文字母,數字,特殊字符(ASCII)和中文,也叫國標(國家標準)。一個英文字母用一個字節表示,一箇中文用兩個字節。
Unicode:萬國碼:把世界上的全部的文字都記錄到這個密碼本。用4個字節表示,可表示2**32=4294967296個文字。
utf-8:(Unicodes升級版本)最少用1個字節表示字符(英語),歐洲用2個字節,中文用3個字節
'咱們12ax' :GBK :8個字節
'咱們12ax' :UTF-8:10個字節
單位的轉換:
8bit = 1byte
1024byte = 1kb
1024kb = 1MB
......GB、TB、PB、EB、 ZB 、YB、 NB
不一樣的編碼方式之間不能相互識別:
-
數據在內存中所有是以Unicode編碼的,可是當數據用於網絡傳輸或者存儲到硬盤中,必須是以非Unicode編碼(utf-八、gbk等)
-
python中的數據從內存(Unicode編碼)存儲到硬盤或進行網絡傳輸時要經歷一個特殊的轉化過程,要轉化爲一個非Unicode編碼類型的特殊數據才能進行傳輸或儲存至硬盤,即bytes類型(內存中的編碼方式:非Unicode)
-
bytes與str的操做方式大部分都是同樣,bytes can only contain ASCII literal characters,bytes用於文件的儲存、網絡的傳輸等。直接將非ASCII碼字符串轉化爲bytes類型會報錯,要使用
encode()
。#ASCII碼中的字符轉bytes: a = b'iloveyou' print(a,type(a)) #b'iloveyou' <class 'bytes'> #將中文轉化爲bytes類型: b = b'山就在那兒' print(b) #SyntaxError: bytes can only contain ASCII literal characters #正確方法爲: c = '山就在那兒' b = c.encode('utf-8') print(c.encode('utf-8')) #通常指定utf-8的編碼形式, (encode:編碼) >>>b'\xe5\xb1\xb1\xe5\xb0\xb1\xe5\x9c\xa8\xe9\x82\xa3\xe5\x84\xbf'
-
bytes可轉化爲字符串類型(Unicode)(decode,解碼)。用什麼編碼類型轉換爲bytes數據類型的就用什麼解碼。
b = b'\xe5\xb1\xb1\xe5\xb0\xb1\xe5\x9c\xa8\xe9\x82\xa3\xe5\x84\xbf' print(b.decode('utf-8')) #山就在那兒 #用什麼編碼類型轉換爲bytes數據類型的就用什麼解碼。 b = b'\xe5\xb1\xb1\xe5\xb0\xb1\xe5\x9c\xa8\xe9\x82\xa3\xe5\x84\xbf' c = b.decode('gbk') print(c) >>>UnicodeDecodeError: 'gbk' codec can't decode byte 0xbf in position 14: incomplete multibyte sequence
小題試作:gbk轉換爲utf-8
#分析,全部的編碼都與Unicode有關(計算機內存中以Unicode編碼),所以可先將gbk轉換爲Unicode編碼,再轉換爲utf-8編碼。 gbk = b'\xc9\xbd\xbe\xcd\xd4\xda\xc4\xc7\xb6\xf9' decode1 = gbk.decode('gbk') #解碼爲Unicode編碼的字符串,可print(decode1)查看。 print(decode1.encode('utf-8')) #以utf-8編碼 >>>b'\xe5\xb1\xb1\xe5\xb0\xb1\xe5\x9c\xa8\xe9\x82\xa3\xe5\x84\xbf'
數據類型
數據類型的分類(可變與不可變):
- 可變(不可哈希)的數據類型:list dict set(集合是無序的,不重複的數據集合,它裏面的元素是可哈希的(不可變類型),可是集合自己是不可哈希(因此集合作不了字典的鍵))
- 不可變的數據類型(可哈希): str bool int tuple(該對象自己不可變),(作不可變類型作任何操做,方法都不會改變原對象)
數據之間類型的轉換
-
int bool str 三者轉換
-
str list 二者轉換
-
list set 二者轉換
-
str bytes 二者轉換,只有字符串能和bytes互換。
-
全部數據均可以轉換成bool值,轉換成bool值爲False的數據類型有:
'',0,(),{},[],set(),None
按照儲存空間的佔用分(從低到高)
- int
- str
- set : 無序
- tuple: 有序,不可變
- list: 有序,可變
- dict: 有序(3.6版本以後),可變
序列化
結構化數據:內存中的數據是結構化數據,存在許多指針(有指向關係的數據)。將數據組合爲有指向關係的數據是結構化的過程。
線性數據:磁盤中的文件是一個典型的線性數據,數據間沒有引用關係。
序列化(serialization):將內存中的數據結構(list、str、tuple、dict、set等)轉換成字節或字符串,用以保存在文件或經過網絡傳輸,稱爲序列化過程。
反序列化(deserilzation):從磁盤文件中,網絡中獲取的數據類型(字節),轉換成內存中原來的數據數據結構,稱爲反序列化過程。
序列化模塊:json、pickle、shelve等
軟件開發規範
配置文件(conf-setting.py):靜態(變量不能改變只能引用)的路徑,數據庫鏈接的設置
主邏輯函數(core-src.py):
公共組件(lib-common):裝飾器、日誌函數、
啓動函數(bin-starts.py):只有一個文件。
數據庫(db-register):文本數據
日誌(log-access.log):日誌的數據
READEME
str
存儲少許的數據。切片仍是對其進行任何操做,獲取的內容全都是str類型,存儲的數據單一。
capitalize() 首字母(第一個單詞)大寫,其他變小寫
title() 每一個單詞的首字母大寫。(以特殊字符(非字母)隔開的即爲一個單詞)
swapcase() 大小寫翻轉
center() 居中,有1個必選參數:寬度,一個非必選參數:填充)
find() 經過元素找索引,找到第一個就返回索引,找不到返回-1。
index() 經過元素找索引,找到第一個就返回索引,找不到就報錯。
list
列表能夠儲存大量的數據,但數據間的關聯性不強且列表的查詢速度比字典慢。
在列表末尾增長一個數據項(list.append()方法),直接修改列表沒有返回值。
在列表末尾增長一個數據項集合( 添加多個元素的方法,迭代追加)(list.extend()方法);
l1 = [1,2] l1.extend('abcd') print(l1) >>>[1, 2, 'a', 'b', 'c', 'd'] l2 = [1,2] l2.extend(['asd',1,2]) print(l2) >>>[1, 2, 'asd', 1, 2]
列表的特殊插入法,在特定位置增長一個數據項(list.insert()方法)不推薦使用,下降性能:
a=['b','c','d'] a.insert(0,'a') a[0:1] #['a']
從列表末尾刪除數據,按照索引刪除(list.pop()方法),若果沒有給出索引值則默認刪除最後列表的一個元素,有返回值,返回刪除的元素,不推薦使用pop(n),下降性能。
l1 = [1,2,3,4] print(l1.pop(0)) #1
在列表中刪除一個特定項(list.remove()方法);若是有重複的則默認刪除第一個元素(從左開始)
list.clear()清空列表的方法,del 按照索引刪除,也能夠按照切片刪除(可加步長),無返回值
l1 = [1,2,3,4,5,6] del l1[0] print(l1) #[2.3.4,5,6] del l1[0:4:2] print(l1) #[3,5,6]
按照索引改元素;按照切片更改元素(迭代增長),也可按照切片加步長更改,但必須一 一 對應。
l1 = [1,2,3,4] l1[0] = 0 l1[1:] = 'abcd' print(l1) >>>[0, 'a', 'b', 'c', 'd'] l1[::2]='123' print(l1) >>>['1', 'a', '2', 'c', '3']
index() 經過元素找索引
sort() 默認從小到大排序,設置reverse參數則可從小到大。
reverse() 反轉
列表相加 (3.4以上版本)
列表與數字相乘 (3.4以上版本)
l1 = [2,'a',[1,'b']] l2 = l1*3 print(l2) >>>[2, 'a', [1, 'b'], 2, 'a', [1, 'b'], 2, 'a', [1, 'b']]
列表的特殊性:正向循環一個列表時若是刪除某個元素,那麼這個元素後面的全部元素都會向前進一位,它們的索引相比以前也會前進一位,所以,在循環一個列表時的過程當中,若是要改變列表的大小(增長值或者刪除值),那麼結果極可能會出錯或者報錯。
l1 = [1,2,3,4,5,6] #刪除列表中索引位爲偶數的元素。 for i in range(0,len(l1),2): l1.pop(i) print(l1) >>>IndexError: pop index out of range
解決此問題有三種方式:
1.直接刪除 (按照元素刪除,按照索引刪除,切片加步長
#切片加步長 l1 = [1,2,3,4,5,6] del l1[1::2] print(l1)
2.倒敘刪除
l1 = [1,2,3,4,5,6] for i in range(len(l1)-1,-1,-2): l1.pop(i) print(l1) >>>[1,3,5] #不能用如下代碼; l1 = [1,2,3,4,5,6] for i in range(1,len(l1),2): l1.pop(-i)
3.思惟轉換
l1 = [1,2,3,4,5,6] l2 = [] for i in range(0,len(l1),2): l2.append(l1[i]) l1 = l2 print(l1)
b=sorted(a,reverse=True) 函數按照長短、大小、英文字母的順序給列表中的元素進行排序,但不會改變列表自己
列表是有序的,能夠用enumerate()函數在索引的時候獲得每一個元素的具體位置:
l1 = ['a','b','c'] for num,int in enumerate(l1): print(num,int) >>>0 a 1 b 2 c
列表的嵌套
列表推導式:用一行代碼構建一個比較複雜有規律的列表。
l1 = [i for i in range(10)] #可將range換爲可迭代對象。
列表推導式可分爲兩類。
- 循環模式:需遍歷每個元素。[變量(加工後的變量) for 變量 in iterable]
#例1:將10之內全部整數的平方寫入列表: l1 = [i*i for i in range(11)] print(l1) #例2:100之內的全部奇數寫入列表: l1 = [i for i in range(1,100,2)] print(l1) #例3:從python1到python10寫入列表: l1 = [f'python{i}' for i in range(1,11)] print(l1)
- 篩選模式:[變量(加工後的變量) for 變量 in iterable if 條件]
#例1:將10之內可以被2整除的數寫入列表 l1 = [i for i in range(11) if i%2==0] print(l1) #例2:過濾列表l1中小於3的字符串,將剩下的轉換成大寫。l1 = ['nonl','globals','as','in'] l1 = ['nonl','globals','as','in'] l2 = [i.upper() for i in l1 if len(i)>=3] print(l2) #例3:將num列表中含有兩個0的字符串生成一個新列表。num = [['021','001','201'],['100','012','010']] l1 = [j for i in num for j in i if j.count('0') == 2] print(l1)
缺點:
- 只能構建計較複雜而且有規律的列表;
- 超過三層循環才能構建成功的,不建議使用列表推導式;
- 沒法找查錯誤(debug模式)
tuple
爲只讀列表。可存大量的數據,能夠索引,切片(按步長),不可更改,但元祖中列表裏的元素能夠按照列表更改,示例: (1, True, [1, 2, 3])。
特殊性:元祖中只有一個元素,而且沒有’,‘,則它不是元祖,它與括號中的數據類型一致
print(type((1,2))) #<class 'tuple'> print(type((1))) #<class 'int'> print(type((1,))) # <class 'tuple'>
count() 計數
index() 找索引
元祖的拆包:分別賦值,必須一 一對應。(列表也能夠拆包,但通常不用)
a,b=(1,2) print(a,b) #1 2 #與*(聚合)使用: a,b,*c = (1,2,3,4,5,6,7,) print(a,b,c) #1 2 [3, 4, 5, 6, 7] a,*b,c = (1,2,3,4,5,6,7,) print(a,b,c) #1 [2, 3, 4, 5, 6] 7 a,*b,c = [1,2,3,4,5,6,7,] print(a,b,c) #1 [2, 3, 4, 5, 6] 7 a,*b,c=range(10) print(a,b,c) #0 [1, 2, 3, 4, 5, 6, 7, 8] 9
字典
鍵必須是不可變的數據類型,最經常使用的是str int,鍵是不能改變且沒法修改,而值能夠改變,可修改,能夠是任何對象。鍵是不能重複的,而值能夠重複。字典在3.5x版本以前(包括3.5)是無序的;字典在3.6x會按照初次創建字典的順序排序的,學術上不認爲是有序的。字典3.7x之後都是有序的。字典以空間換時間,查詢速度很是快,內存消耗巨大。
字典的建立方式:
dic = dict((('i',1),('love',2),('you',3))) #用列表也能夠dic = dict([('i',1),('love',2),('you',3)]),列表或元祖中的每一個元素是一個二元組就能夠用dict()轉換爲字典。 print(dic) >>>{'i': 1, 'love': 2, 'you': 3} dic = dict(i=1,love=2,you=3) print(dic) >>>{'i': 1, 'love': 2, 'you': 3} dic = dict({'i': 1, 'love': 2, 'you': 3}) print(dic) >>>{'i': 1, 'love': 2, 'you': 3} #字典推導式 dic = {i:i+1 for i in range(3)}
增,添加多個元素的方法:
update():有鍵則改,無鍵則增長。
setdefault():有則不變,無則增長
fromkeys():第一個參數必須爲可迭代對象,可迭代的對象共用第二個參數(id相同)。
dic = {} dic['age'] = '18' dic.setdefault('able') print(dic) #{'able':None} dic.setdefault('hobby':python) dic = dict.fromkeys('abc',1) print(dic) #{'a': 1, 'b': 1, 'c': 1} dic = dict.fromkeys([1,2,3],[]) print(dic) #{1: [], 2: [], 3: []} dic[1].append('a') print(dic) #{1: ['a'], 2: ['a'], 3: ['a']}
刪:
popitem() 3.5版本以前,隨機刪除,3.6版本以後,刪除最後一個,有返回值。
pop(),按照鍵刪除,有返回值,返回的爲字典的值,若是沒有要刪除的鍵,則會報錯,但能夠設置第二個兩個參數,不管字典中是否有此鍵,都不會報錯,如有此鍵則返回值爲此鍵的值,若無此鍵則,返回設置的參數。
del,若無鍵會報錯,不推薦使用
clear 清空
dic = {} print(dic.pop('hobby','沒有此鍵值對')) #沒有此鍵值對。 del dic['age'] #報錯 dic.claer()
改
dic['name'] = 18
update() 有則覆蓋,無則增長
#1.增長/改鍵值對 dic0 = {1:'i'} dic1 = {3: 'c'} dic0.update(a='love') #a位置不能是int類型 dic0.update(dic)
查
dic = {'name':'山就在那兒','hobby_list':['book','python']} print(dic['hobby_list']) #若沒有此鍵則會報錯,不建議用 l1 = dic.get('hobby_list') #若沒有鍵則會返回None,能夠定義第二個參數,第二個參數即爲返回值 #keys() print(dic.keys()) #會返回一個dict_keys類型,包含字典中全部的鍵,和列表類似,但不能索引,轉化成列表後可遍歷 >>>dict_keys(['name', 'hobby_list']) #values() print(dic.values()) #會返回一個dict_values類型,包含字典中全部的值,和列表類似,但不能索引,可轉化成列表後可遍歷 >>>dict_values(['山就在那兒', ['book', 'python']]) #items() for k,v in dic.items(): #元祖的拆包 for i in dic.items() print(i) 打印的結果數據爲元祖 #小題試作:將字典dic中的以‘k’開頭的鍵值對刪除,(循環一個字典時,若果改變字典的大小則或報錯。) dic = {'k1':'a','k2':'b','k3':'c','a':'d'} l1 = [] for key in dic: if key.startswith('k'): l1.append(key) for i in l1: dic.pop(i) #改進 for key in list(dic.keys()): #將其轉換爲一個列表,若不加list則會報錯。 if 'k' in key: dic.pop(key)
字典的嵌套
字典推導式
l1 = ['1','2','3'] l2 = ['一','二','三'] dic = {l1[i]:l2[i] for i in range(len(l1))}
set
集合:容器型數據類型,要求它裏面的元素是不可變的數據(可哈希),但它自己是可變的數據類型(不可哈希)。集合是無序的。以{}存放數據。
做用:列表的去重;關係的測試(交集,並集…)
集合的建立:
set = {1,2,'a'} #空集合的表示: set1 = set() #set1 = {}表示空字典
增:add()、update():迭代增長,有重複的則去重
set1 = {1,2} set1.add('a') set1.update('asdfdsa') print(set1) >>>{'a', 1, 2, 'f', 's', 'd'}
刪:remove()(按照元素刪除,pop()隨機刪除,clear()清空集合,del 刪除集合,discard()有則刪除,無則pass,不報錯。
改:先刪除再增長
交集。(&或者intersection) 集合共同有的元素
set1 = {1,2,3} set2 = {2,3,4} print(set1 & set2) #or print(set1.intersection)
並集。(|或者union)集合全部的元素
set1 = {1,2} set2 = {2,3} print(set1 | set2) #or print(set1.union(set2))
差集 ( - 或者difference) ,前一集合獨有的元素
set1 = {1,2,3,4,5} set2 = {2,4,6} print(set1 - set2) #or print(set1.difference(set2))
反交集。(^ 或者symmetric_difference)每一個集合獨有的元素
set1 = {1,2,3,4,5} set2 = {3,4,5,6,7} print(set1 ^ set2) #or print(set1.symmetric_difference(set2)) >>>{1,2,6,7}
子集與超集
set1 = {1,2} set2 = {1,2,3} print(set1 < set2) #or print(set1.issubset(set2)) #or print(set2.issuperset(set1)) >>>True #set1是set2的子集 print(set2 > set1) >>>True #set2是set1的超集
frozenset()讓集合變爲不可變類型
s = frozenset('qweasd') print(s,type(s)) >>>frozenset({'q', 'e', 'w', 's', 'a', 'd'}) <class 'frozenset'>
S集合推導式
set1 = {i for i in range(10)}
正則表達式
- 檢測一個輸入的字符串是否合法。(web開發項目 表單驗證)
- 從一個大文件中找到全部符合規則的字符串。(日誌分析、爬蟲)
在正則表達式中表示匹配內容的符號是正則中的元字符,匹配次數的是量詞正則都是從左至右依次匹配。
字符組:[]
,描述的是一個位置上出現的全部的可能性。一個字符組只能匹配一個字符。根據ASCII進行匹配。[0-9] 、[a-z]、[A-Z](ASCII碼值:Z爲91,a爲97)。能夠從小到大匹配,但不能從大到小匹配,如:[Z-a]。
[A-Z]
:a-Z中間有一些特殊字符,所以不用此方法匹配所有的大小寫字母。[a-zA-Z]
:匹配所有的大小寫字母。[0-9a-zA-Z]
\d
:digit,表示匹配任意的一個數字,同[0-9]
,或[\d]
。
\w
:word(標識符,變量名),表示匹配任意的一個數字、字母、下劃線,同[0-9a-zA-Z_]
。
\s
:匹配全部的一個空白符(空格、Tab、Enter),同(‘ ’、‘\t’、‘\n’)
\t
:匹配Tab。
\n
:匹配Enter,即回車。
\W
:匹配任意一個的非數字、字母、下劃線。
\D
:匹配任意的一個非數字。
\S
:匹配一個非空白符。
[\d\D]
:匹配全部,同[\w\W]、[\s\S]。
.
:正常狀況匹配除換行符以外的一切字符。但在re模塊中可用參數更改成匹配全部。
非字符組:[^]
[^\d]
:匹配全部的非數字。
^
:表示匹配一個字符串的開始。
$
:表示匹配一個字符串的結尾。^於&一般用於約束。
a表達式|b表達式
:能匹配a或b表達式裏的內容,若是a匹配成功了就不會匹配b,全部若是兩個規則有重疊的部分老是將表達式長的放在左面。
()
:分組,約束元字符的做用範圍,常常用於|。
\
:轉義字符,本來有特殊意義的字符要表達它自己的意義時,須要轉移;有一些特殊意義的內容,放在字符組中,會取消它的特殊意義,如[. () * + ?]
只能表示它特殊的意義。-
放在兩值之間表示兩值的範圍,容不須要表示範圍,須要加\
,-
若放在字符組的開頭或結尾就只表示-
。
\b
:匹配單詞的邊界。找以ing結尾的單詞ing\b
。
量詞:{n}:表示匹配n次。
{n,}
:表示匹配至少n次。
{n,m}
:表示至少匹配n次,至多匹配m次。
?
:表示匹配0次或1次,同{0,1}
+
:表示匹配一次或屢次,同{1,}
*
:表示匹配0次或屢次,同{0,}
例子:匹配整數\d+
,匹配小數\d+\.\d+
,匹配一個整數或小數\d+\.?+\d*
但此時會出現問題,如12.
也會被匹配上。這時能夠用分組:\d(\.\d+)?
匹配一個手機號碼:^1[3-9]\d{9}$
貪婪匹配:會在量詞範圍容許的狀況下儘可能向多的方向匹配。根據回溯算法,從左至右依次按照規則匹配,若規則不知足最後的條件,就會回溯,形成貪婪模式。.*x
表示匹配任意字符,任意屢次,遇到最後一個x才停下來。
惰性匹配:非貪婪匹配,在量詞後加上?
,會在量詞範圍容許的狀況下儘可能少的匹配。.*?x
表示匹配任意字符,任意屢次,一旦遇到x就停下來。
匹配一個18位或者15位的身份證號:
^[1-9]\d{16}[\dx]$|^[1-9]\d{14}$
或
^([1-9]\d{16}[\dx]|[1-9]\d{14})$
或
^[1-9]\d{14}(\d{2}[\dx])?$
代碼塊、緩存機制
id、is、==
-
id: 數據的內存地址具備惟一性,若id 相同則爲同一個數據。id():獲取數據的內存地址(隨機的地址:內存臨時加載,存儲數據,當程序運行結束後,內存地址即被丟棄)
-
is 判斷id是否相同,==:判斷值是否相同。id相同,值必定相同,值相同,id不必定相同。
l1 = [1,2,3] l2 = [1,2,3] print(l1 == l2) #True #比較的是兩邊的值是否相等。 l1 = [1,2,3] l2 = [1,2,3] print(l1 is l2) #False 判斷的是(id)是否相同。 s1 = 'iam' s2 = 'iam' print(s1 is s2) #True 與同一代碼塊下的緩存機制有關 l1 = [1,2,3] l2 = l1 print(l1 is l2) #True
代碼:python的程序是由代碼塊構造的。塊是一個python程序的腳本,它是做爲一個單元執行的。一個模塊,一個函數,一個類,一個文件等都是代碼塊。而做爲互交命令方式輸入的每一個命令都是一個代碼塊。
代碼塊的兩個緩存機制:若是在同一代碼塊下,則採用同一代碼塊下的換緩存機制。若是是不一樣代碼塊,則採用小數據池的駐留機制。優勢:須要值相同的字符串,整數的時候,直接拿來用,避免頻繁的建立和銷燬,提高效率,節約內存
同一個代碼塊的緩存機制:Python在執行同一個代碼塊的初始化對象的命令時,會檢查是否其值是否已經存在,若是存在,會將其重用。換句話說:執行同一個代碼塊時,遇到初始化對象的命令時,它會將初始化的這個變量與值存儲在一個字典中,在遇到新的變量時,會先在字典中查詢記錄,若是有一樣的記錄那麼它會重複使用這個字典中的以前的這個值,即:id相同。
-
適用對象:
int:任何數字在同一代碼塊下都會複用。
str:幾乎全部的字符串都會符合緩存機制
bool:True和False在字典中會以1,0方式存在,而且複用。s1 = 'iam' s2 = 'iam' print(s1 is s2) >>>True
在不一樣一個代碼塊內的緩存機制:小數據池,也稱爲小整數緩存機制,或者稱爲駐留機制等等。Python自動將-5~256的整數進行了緩存,將必定規則的字符串在字符串駐留池中建立一份。不管是緩存仍是字符串駐留池,都是python作的一個優化,就是將~5-256的整數,和必定規則的字符串,放在一個‘池’(容器,或者字典)中,不管程序中哪些變量指向這些範圍內的整數或者字符串,那麼它直接在這個‘池’中引用,並不會從新建立對象,而是使用已經建立好的緩存對象。言外之意,就是內存中之建立一個。
-
適用對象:
int(float):對於整數,小數據池的範圍是-5~256 ,若是多個變量都是指向同一個(在這個範圍內的)數字,他們在內存中指向的都是一個內存地址。
str:字符串的長度只含有大小寫字母,數字,下劃線時,纔會默認駐留。
bool值:True、False,不管建立多少個變量指向True,False,那麼它在內存中只存在一個。#經過交互方式中執行下面代碼,這是不一樣代碼塊下,則採用小數據池的駐留機制。 >>> i1 = 1000 >>> i2 = 1000 >>> print(i1 is i2) False # 不一樣代碼塊下的小數據池駐留機制 數字的範圍只是-5~256. #4.指定駐留 >>>from sys import intern >>>a = intern('hello!@'*20) >>>b = intern('hello!@'*20) >>>print(a is b) True #指定駐留是你能夠指定任意的字符串加入到小數據池中,讓其只在內存中建立一個對象,多個變量都是指向這一個字 # 雖然在同一個文件中,可是函數自己就是代碼塊,因此這是在兩個不一樣的代碼塊下,知足小數據池(駐存機制),則指向兩個不一樣的地址。
def func():
i1 = 1000
print(id(i1)) # 2288555806672
def func2():
i1 = 1000
print(id(i1)) # 2288557317392
func()
func2()
```
深淺copy
淺copy:嵌套的可變的數據類型是同一個。深copy:嵌套的可變的數據類型不是同一個。
淺copy,列表是一個一個的槽位,它儲存的是對象的內存地址。淺拷貝仍然使用原來的對象的內存地址。對儲存的可變對象能夠進行更改;若改變不可變類型,則改變的不是不可變類型自己,而是變量的指向關係,切片是淺copy。
l1 = [1,2] l2 = l1 l1.append(3) print(l2) >>>[1,2,3] #l1,l2兩變量指向同一個id(數據) #淺copy l3 = [1,2,['a']] l4 = l3.copy() l3.append(3) print(l3) >>>[1,2,['a'],3] print(l4) >>>[1,2,['a']] l3[-2].append(4) #or l4[-1].append(4) print(l3) >>>[1, 2, ['a', 4], 3] print(l4) >>>[1,2,['a',4]] #l4與l3列表中的數據id是相同的,但l4與l3列表id不相同,即l3中的每一個元素與l4中的每一個元素使用的是相同的一個id,但l4與l3用的不是同一個id。
深copy,需導入模塊copy。深拷貝會給對象建立一個新的內存地址,python對深copy作了一個優化,不可變數據類型對象的內存地址沿用同一個,只爲可變數據類型對象再從新建立內存地址。
import copy l3 = [1,2,['a']] l4 = copy.deepcopy(l3) l3[-1].append(3) print(l3) >>>[1,2,['a',3]] print(l4) >>>[1,2,['a']]
文件的操做
open() 內置函數,open底層調用的是操做系統的接口。有三個參數:1. 文件路徑 (文件夾路徑+文件名+文件類型) 2. 編碼方式:encoding,參數不寫會以操做系統默認的編碼本打開,windows默認編碼:gbk(windows10是utf-8,Linux:utf-8,mac:utf-8)。3.模式:mode,須要有兩個方向給予指定:打開文本的模式、打開文件的方向,默認不寫第一個方向則以讀(r)的方式打開,默認不寫第二個則以文本模式打開。
文件的方向:t(text,文本模式)、b(binary,字節模式)。
文件的模式:a(append,追加)、w(write,寫)、r(read,讀)
文件句柄:變量,通常在文件操做是設置的約定俗成的變量,也有寫做爲f1,fh,file_handler,f_h等,也被稱爲文件句柄。經過對文件進行的任何操做都要做用於文件句柄(如fl.raed())。
常見報錯緣由:
-
UnicodeDecodeError:文件儲存時與文件打開時編碼本運用不一致。
-
路徑分隔符產生問題:\ (反斜槓) 有轉義符的意思。解決方法:在文件路徑前加r,讓轉義符失效,也可以使用兩次轉義:
\\
。
文件的讀:
rt、rb、r+、r+b等。
-
read() 若括號中無參數則一次所有讀出,若寫參數(數字)則能夠按照字符(從1開始)讀取,文件中的換行符算做一個字符。
-
readline() 若括號中無參數則讀一行,若寫參數(數字)則能夠按照字符(從1開始)讀取字符(同read),文件中的換行符算做一個字符。注意,文本中有換行符,而print()函數也默認換行。
-
readlines() 若括號中無參數讀取全部行,返回列表,列表的每一個元素爲源文件的每一行,若寫參數(數字)則能夠按照每一行讀取。
-
循環讀取,文件句柄可遍歷(文件句柄是迭代器,每次for循環時都只讀取文件一行,節省內存,而read,readlines等是一次讀取至內存中,若果文件過大,則會出現問題)。
f = open(r'd:\python.txt',encoding='utf-8') for line in f: print(line) f.close() #每次操做文件後必定要關閉。
-
rb:操做的是非文本的文件,好比:圖片,視頻,音頻等。rb模式不用encoding參數。
fl = open(r'd:\雪景.jpg',mode='rb') #雪景.jpg是張照片
以b方式打開,字節沒有行的概念。若文件過大最好設置每次讀取多少字節
-
r+ 讀寫功能(讀並追加),必須先讀或將指針調製文檔末尾才能追加不然按照字節覆蓋,極易出現亂碼問題或報錯。
文件的寫:
- wt、wb、w+、w+b等。
- w,wb: 若已有相同的文件則會先清空原有文件的內容再寫入 ,若無則建立。清空:打開文件後會先清空原文件再寫入。
文件的追加:
- at、ab、a+、a+b等。
- a:若無文件則會建立文件。如有則直接在原文件後追加
指針:
- 文件的讀取都有指針定位,文件中的指針起始位置在文件最前面。
- tell():讀取指針的位子,以****字節**爲單位(utf-8編碼:一箇中文三個字節,一個字母1個字節)
seek():調整光標的位置,以字節爲單位
flush():強制刷新(保存),通常在寫文件時使用,在寫後通常要對文件句柄使用flush方法,以避免保存失敗。
打開文件的另外一種方式
-
with open() as f1:
優勢:不用手動關閉文件句柄,會在必定時間內關閉;一個with能夠操做多個文件。#打開多個文件: with open(r'd:\text.txt',encoding='utf-8',mode='a') as f1,open(r'd:\python.txt',encoding='utf-8',mode='a') as f2:
各大操做文件的軟件(word、筆記本等等)底層都以如下基本方式操做文件:
- 以讀的模式打開原文件
- 以寫的模式建立一個新文件
- 將原文件的內容讀出來修改爲新的內容,寫入新文件
- 將原文件刪除
- 將新文件重命名
小題試練:將d盤下的python.txt文件中的小寫o全變爲大寫。
import os #引入os模塊 #1.以讀的模式打開原文件 #2.以寫的模式建立一個新文件 with open(r'd:\python.txt',encoding='utf-8') as f1,\ open(r'd:\python.bak',encoding='utf-8',mode='w') as f2: #.bak是一種備份文件類型 #3.將原文件的內容讀出來修改爲新的內容,寫入新文件 for old_line in f1: new_line = old_line.replace('o','O') f2.write(new_line) #4.將原文件刪除 os.remove('d:\python.txt') #5.將新文件重命名 os.rename('d:\python.bak','d:\python.txt')
函數
return: 在函數中遇到return直接結束函數;能夠給函數外部返回一個返回值,將數據返回給函數的執行者,調用者。返回值可被print打印出,若無返回值,print會打印出None。return可返回多個值,會以元祖的形式將多個元素返回給函數的執行者。(元祖能夠進行拆包)
參數:實參,函數的執行時傳入的參數。形參,函數的定義時接受的參數
實參角度:
-
位置參數:按照實參位置參數與形參的對應順序(從左到右)依次傳入。
-
關鍵字參數
-
混合傳參
形參角度:
- 位置參數
- 默認參數(默認值參數)
- 萬能參數:*args 接受全部的位置參數 ; **kargs 接受全部的關鍵字參數。
- 僅限關鍵字參數(3.4版本之後):只能接受關鍵字參數。若設置此參數必須給此參數傳入值,不然會報錯。此參數只能在*args與**kargs之間。
形參的順序:調用函數時,會自動從左至右傳入參數。形參的順序以下:1.位置參數 2.*args 3.默認參數 3. 僅限關鍵字參數 4.**kargs。僅限關鍵字參數與默認參數可互換。
def func(a,b,*args,c='',d,**kargs): pass #a,b:位置參數 d:僅限關鍵字參數,只能有關鍵字參數爲其傳參。
函數不能改變全局不可變的變量,可變數據仍然可改變。
l1 = [1,2] def a(l1): l1.pop(1) a(l1) print(l1)
匿名函數(用lambda構建):結構比較簡單的函數。形式:lambda 參數 : 返回值
def func(a,b): return a+b #構建匿名函數: func1 = lambda a,b:a+b print(func1(1,2))
lambda 參數 : 返回值:lambda後直接加形參,形參加多少均可以,但通常只用位置參數,參數之間須要用」,「隔開。
#例1:接受一個可切片的數據,以元祖形式返回索引位0與2的對應元素 func = lambda a:(a[0],a[2]) #例2:接收兩個int參數,將較大的數據返回。 func = lambda a,b:a if a>b else b
命稱空間
在python解釋器開始執行以後, 就會在內存中開闢一個空間, 每當遇到一個變量的時候, 就把變量名和值之間的關係記錄下來, 可是當遇到函數定義的時候, 解釋器只是把函數名讀入內存, 表示這個函數存在了, 至於函數內部的變量和邏輯, 解釋器是不關心的. 也就是說一開始的時候函數只是加載進來, 只有當函數被調用和訪問的時候, 解釋器纔會根據函數內部聲明的變量來進行開闢變量的內部空間,函數中的變量只能在函數內部使用。隨着函數執行完畢, 這些函數內部變量佔用的空間也會隨着函數執行完畢而被清空。咱們給這個‘存放名字與值的關係’的空間起了一個名字-------命名空間。
全局名稱空間:代碼在運行伊始,建立的存儲'變量名與值的關係'的空間叫作全局命名空間。即 在py文件中, 除函數外聲明的變量都屬於全局命名空間。程序不結束,全局名稱空間不會消失。
局部名稱空間:在函數的運行中開闢的臨時的空間叫作局部命名空間也叫作臨時名稱空間,臨時存放函數中的變量與值的關係。即在函數中聲明的變量會放在局部命名空間。函數結束時局部命名空間就會消失。
內置名稱空間:內置名稱空間存放的就是一些內置函數等拿來即用的特殊的變量:input,print,list等。
加載順序:這個三個空間在內存中建立的前後順序,他們不能同時建立,在啓動python解釋器以後,即便沒有建立任何的變量或者函數,仍是會有一些函數直接能夠用,所以在啓動Python解釋器的時候,一些函數就已經導入到內存當中供咱們使用,因此是先加載內置名稱空間,而後就開始從文件的最上面向下一行一行執行,此時若是遇到了初始化變量,就會建立全局名稱空間,將這些對應關係存放進去,而後遇到了函數執行時,在內存中臨時開闢一個空間,加載函數中的一些變量等等。因此這三個空間的加載順序爲:內置命名空間(程序運行伊始加載)->全局命名空間(程序運行中:從上到下加載)->局部命名空間(程序運行中:調用時才加載。
取值順序:若是在全局名稱空間引用一個變量,會先從全局名稱空間引用,全局名稱空間若是沒有,纔會向內置名稱空間引用。 若是在局部名稱空間引用一個變量,先從局部名稱空間引用,局部名稱空間若是沒有,纔會向全局名稱空間引用,全局名稱空間再沒有,就會向內置名稱空間引用。
因此取值順序知足的就近原則,從小範圍到大範圍一層一層的逐步引用且單向不可逆,即LEGB原則。(L:lcoal E:eclose G:global B:builtin)
做用域:做用域就是做用範圍, 按照生效範圍來看分爲全局做用域和局部做用域
- 全局做用域: 包含內置命名空間和全局命名空間. 在整個文件的任何位置均可以使用(遵循從上到下逐⾏執行)。全局做用域: 全局命稱空間 + 內置命稱空間
- 局部做用域: 在函數內部可使用。2. 局部做⽤域: 局部命稱空間,局部做用域能夠引用全局做用域的變量但不能改變全局變量(當python解釋器讀取到局部做用域時,若發現有對一個變量進行修改的操做,解釋器會認爲你在局部已經定義過這個局部變量了,因而就在局部找這個局部變量,若沒有就會報錯,沒法改變全局變量),全局做用域不可義引用局部做用域的變量。
內置函數: globals() local()
-
globals(): 以字典的形式返回全局做用域(內置命稱空間以及全局命稱空間的全部內容)全部的變量對應關係。
-
locals(): 以字典的形式返回當前做用域的變量全部的對應關係。
-
函數的嵌套(高階函數):
關鍵點:只要碰見了函數名+()就是函數的調用. 若是沒有就不是函數的調用。請說出下面代碼的執行順序:#例1: def func1(): print('in func1') print(3) def func2(): print('in func2') print(4) func1() print(1) func2() print(2) # 例2: def func1(): print('in func1') print(3) def func2(): print('in func2') func1() print(4) print(1) func2() print(2) # 例3: def func2(): print('1in func2') def func3(): print('in func3') print('2in func2') func3() print('3in func2') print(3) func2() print(5)
默認參數的陷阱(只針對於默認參數是可變的數據類型):若是默認參數使用的是可變類型數據,那麼不管調用多少次這個默認參數,都是同一個(id相同)。默認參數的可變數據類型既不在全局也再也不局部,定義後不會消失。
def func(num,nums=[]): nums.append(num) return nums ret1 = func(1) print(ret1) #[1] ret2 = func(2) print(ret2) #[1,2] 將第一次的數據也包含了。 #例: def func(a,list=[]): list.append(a) return list ret1 = func(10,) print(ret1) #[10] print(func(20,[])) #[20] #從新爲列表傳入參數 print(func(100)) #[10,100] print(ret1) #[10,100]
局部做用域的陷阱:在函數中,若是定義一個變量,可是在定義變量以前改變這個變量,即便全局變量有此引用的變量,仍然會報錯。
#例1: count = 1 def func(): count += 1 print(count) func() IndentationError: unindent does not match any outer indentation level #例2: count = 1 def func(): print(count) func() #1 #例3: count = 1 def func(): print(count) count = 1 func() UnboundLocalError: local variable 'count' referenced before assignment
global:在局部做用域裏聲明一個全局變量。
#1. def func(): global num num = 1 print(num) #會報錯。 #2. num = 0 def func(): global num num = 1 func() print(num) #1
nonlocal:不可以操做全局變量;主要用於內層函數對外層函數的局部變量進行修改。
def func1(): count = 1 def inner(): nonlocal count count+=1 inner()
函數名的運用
- 函數名指向的是函數的內存地址
函數名+()
就能夠執行函數 - 函數名能夠做爲容器類數據類型的元素
- 函數名能夠做爲函數的參數
- 函數名能夠做爲函數的返回值。
迭代器
可迭代對象(iterable):
對象:python中一切皆對象。 可迭代:能夠進行循環更新的一個值。以專業角度來講,內部含有__iter__
方法的對象即爲可迭代對象。如:str、list、tuple、dict、set、range等。
獲取對象的全部方法而且以字符串的形式表現:dir()
s1 = 'qwer' print(dir(s1))
判斷一個對象是不是可迭代對象:
s1 = 'qwer' print('__iter__' in dir(s1)) #True
可迭代對象的優勢:
- 存儲的數據可以直接顯示,比較直觀。
- 擁有的方法比較多,操做起來方便。
可迭代對象的缺點:
- 佔內存。
- 不能直接經過for循環(不能直接取值),python內部自動將其轉換爲迭代器而後再進行for循環(用next()方法取值)。
迭代器:
迭代器的定義:內部含__iter__
和__next__
方法的對象就是迭代器,可判斷是否爲可迭代器。文件句柄是常見的迭代器。
可迭代對象能夠轉換爲迭代器iter()
或.__iter__()
:
s1 = 'qwert' obj = iter(s1) #或者: s1.__iter__() print(obj) #會返回一個迭代器的內存地址
對迭代器進行取值next()
或.__next__()
:
迭代器優勢:
- 節省內存,迭代器在內存中至關於只佔一個數據的空間,由於每次取值上一條數據都會在內存釋放。迭代器具備惰性機制,next一次,只取一個值,毫不多取。
迭代器的缺點:
- 不能直觀的查看裏面的數據。
- 只能一直向下取值。
- 速度慢。
可迭代對象與迭代器的對比:
- 可迭代對象是的操做方法比較多,比較直觀,儲存數據相對少(幾百萬個數據,8G內存是能夠承受的)的一個數據集。當側重於對於數據能夠靈活處理,而且內存空間足夠,可將數據設置爲一個可迭代對象。
- 迭代器是很是節省內存,能夠記錄取值位置,能夠經過循環加next方法取值,可是不直觀,操做方法比較單一的一個數據集。當數據量過大,可選擇將數據設置爲一個迭代器。
用while循環模擬for循環對可迭代對象進行取值:
l1 = [1,2,3,4,5,6,7,8] obj = iter(l1) while 1: try: #try:異常處理 print(next(obj)) except StopIteration: break
生成器
生成器:python社區把生成器與迭代器當作同一種,生成器的本質就是迭代器。惟一的區別是:生成器是咱們本身用python代碼構建的數據結構,迭代器都是python提供的,或者轉化的。
獲取生成器的方法:
- 生成器函數
- 生成器表達式
- python內部提供的。
生成器函數獲取生成器,yield:
def func(): print(1) print(3) yield 5 print(func) #<function func at 0x000001A3CCA04438> 仍然是一個函數 ret = func() print(ret) #<generator object func at 0x000002471A631D48> #generator object#生成器對象 #一個next對一個yield的返回值,若是再次調用yield就會接着上次的next. def func(): for i in range(1000000): yield i ret = func() for i in range(5): print(next(ret)) for i in range(10): print(next(ret)) #0, 2,3,4,5,6,7,8,9,10,11,12,13,14
yield與return
return:一個函數中只能存在一個return結束函數的調用,而且給函數的執行者返回值。
yield:只要函數中有yield那麼它就是生成器函數,生成器函數中能夠存在多個yield,一個next對一個yield的返回值,yield不會結束函數的調用,但return會結束函數的調用。
yield from(3.4版本之後),將數據變成一個迭代器返回,生成器函數能夠直接用for循環
def func(): l1 = [1,2,3] yield from l1 #將這個列表變成了一個迭代器返回 for i in func(): print(i) #1 2 3
生成器表達式,生成器表達式:與列表推導式的寫法幾乎同樣,生成器也有循環模式和篩選模式,只是將[]變爲()。但比列表推導式更節省空間。
l1 = (i for i in range(10)) print(l1) #<generator object <genexpr> at 0x000001BB028F8CC8> print(next(l1)) #0 for i in l1: #可直接用for循環,由於for循環自己就是將可迭代對象變爲迭代器再循環。 print(i)
list能夠將生成器中的全部元素添加到列表中
def func(*args): for i in args: for j in i: yield i print(list(func('asdf',(1,2,3)))) #簡化上述函數: def func(*args): for i in args: yield from i #優化了內層循環,提升了運行效率。
遞歸函數
遞歸報錯:RecursionError
官方文檔遞歸最大深度爲1000層,爲了節省內存空間,不讓用戶無限開闢內存空間。但實際上不是1000層。
遞歸的實現效率比不用遞歸的實現效率低。
count = 0 def func(): global count count += 1 print(count) func() print('***') #不會被執行。 func()
循環和遞歸的關係:遞歸比循環更消耗內存。
遞歸的最大深度能夠本身設置:
import sys sys.setrecursionlimit(100000) count = 0 def func(): global count count += 1 print(count) func() func()
但並不會無限制按照設置的參數的開闢內存空間,達到必定限度後會自行中止,而這個限度與電腦有關。
設置條件,讓遞歸函數停下來。
def main(n): print('進入第%d層夢境'%n) if n == 3: print('到達潛意識區,原來我最愛的人是你!開始醒來') else: main(n+1) print('從第%d層夢境醒來'%n) main(1)
並非函數中有return
,return
的結果就必定可以在調用函數的外層接受到
def func(count): count+=1 if count == 5:return 5 func(count) print(func(1)) #None def func(count): count+=1 if count == 5:return 5 return func(count) #調用後用return print(func(1))
遞:一直往深處走,歸:從深處返回。
例題:
1.計算階乘。
def func(n): return n*func(n-1) if n>1 else 1 print(func())
2.os模塊,查看一個文件夾下的全部文件,不能用walk。
import os def show_file(path): name_list = os.listdir(path) print(name_list) for name in name_list: abs_path = os.path.join(path,name) if os.path.isfile(abs_path): print(abs_path) elif os.path.isdir(abs_path): show_file(abs_path) show_file('D:\WorkSpace\Python')
3.os模塊,計算一個文件加下全部文件的大小。
import os def dir_size(path): size = 0 name_lst = os.listdir(path) for name in name_lst: abs_path = os.path.join(path, name) if os.path.isdir(abs_path): ret = dir_size(abs_path) size += ret elif os.path.isfile(abs_path): size+=os.path.getsize(abs_path) return size print(dir_size('D:\WorkSpace\Python')/(1024*1024))
3.計算斐波那契數列,找第100個數。
def func(n): x = n-1 return func(n-1) +func(n-2) if x>1 else 1 print(func(100)) #很是浪費時間,在遞歸時儘可能避免屢次調用函數。 def fib(n,a=1,b=1): if n-1<1: return b else: a,b=b,a+b return fib(n-1,a,b) print(fib(100)) #採用while循環 def fib(n): a,b = 1,1 while n>2: a,b = b,a+b n -= 1 return b print(fib(100)) #採用生成器: def fib(): a = 1 yield a b = 1 yield b while True: a,b = b,a+b yield b import time for i in fib(): print(i) time.sleep(0.01)
4.三級菜單。
menu = { '北京': { '海淀': { '五道口': { 'soho': {}, '網易': {}, 'google': {} }, '中關村': { '愛奇藝': {}, '汽車之家': {}, 'youku': {}, }, '上地': { '百度': {}, }, }, '昌平': { '沙河': { '北航': {}, }, '天通苑': {}, '回龍觀': {}, }, '朝陽': {}, '東城': {}, }, '上海': { '閔行': { "人民廣場": { '炸雞店': {} } }, '閘北': { '火車戰': { '攜程': {} } }, '浦東': {}, }, '山東': {}, } def threeLM(dic): while True: for k in dic:print(k) key = input('input>>').strip() if key == 'b' or key == 'q':return key elif key in dic.keys() and dic[key]: ret = threeLM(dic[key]) if ret == 'q': return 'q' threeLM(menu)
內置函數
python提供了68個內置函數:abs() enumerate() filter() max() min() open() range() print() len() list() dict() str() float() reversed() set() sum() tuple() type() zip() dir() classmethod() delattr() getattr() issubclass() isinstance() object() property() setattr() staticmethod() super() all() any() bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int() locals() next() oct() ord() pow() repr() round()
eval():剝去字符串的外衣(引號),運算裏面的代碼,有返回值。工做時最好不用,容易中病毒。
s1 = '1+1' print(s1) #1+1 print(eval(s1),type(eval(s1))) #2 <class 'int'>
exec():與eval()幾乎同樣,可是它是處理代碼流的。工做時最好不用,容易中病毒。
msg = """ for i in range(5): print(i)""" exec(msg) #0,1,2,3,4
hash:獲取一個對象(可哈希對象:int,str,bool,tuple)的哈希值。哈希值:加密算法之間須要哈希值,與哈希算法有關。
print(hash('12'))
help():打印/獲取一個對象的使用方法。
print(help(str)) print(help(str.upper))
callable():判斷一個對象是否可調用,真爲True,假爲False。
l1 = [1,2] def func(): pass print(callable(l1)) #False print(callable(func)) #True
int():將字符串類型轉換爲int類型;取整(舍尾法)
float():將int和str轉換爲float。
list():將一個可迭代對象轉換成列表
tuple():將一個可迭代對象轉換成元組
dict():經過相應的方式建立字典。
abs():返回絕對值
sum():求和
reversed():將一個序列翻轉,返回翻轉序列的迭代器。可用於字符串。與列表的方法l1 .reverse()
區分.
complex:建立一個值爲real+imag*j的複數;轉換一個str或int爲複數,若是第一個參數爲str則不須要傳遞第二個參數。(複數:complex)
print(complex('1')) #(1+0j)
bin:將十進制數轉換爲二進制字符串並返回。
oct:將十進制數轉換爲八進制字符串並返回。
hex:將十進制數轉換爲十六進制字符串並返回。
divmod:計算除數與被除數的結果,返回一個包含商和餘數的元祖(a//b,a%b)
round:保留浮點數的位數,默認保留整數。
pow:求x**y的次冪,並能夠對所求結果對第三個參數取餘
print(pow(2,2)) #2**2 4 print(pow(2,2,3)) #(2**2)%3 1
bytes:用於不一樣編碼之間的轉換。
s1 = '你好' bs1 = s1.encode('utf-8') print(bs1) #b'\xe4\xbd\xa0\xe5\xa5\xbd' s2 = bs1.decode('utf-8') print(s1) #你好 s3 = '你好' bs2 = bytes(s3,encoding='utf-8') print(bs2) #b'\xe4\xbd\xa0\xe5\xa5\xbd' bs3 = str(bs2,encoding='utf-8') print(bs3) #你好
ord():輸入字符找該字符編碼的位置。(若是在ASCII碼中就用ASCII碼,不然用Unicode)
chr():輸入位置數字找出其對應的字符。(若是在ASCII碼中就用ASCII碼,不然用Unicode)
repr():返回一個對象的string形式。(str帶有引號的形式),在格式化輸出時經常使用(%r)。
s1 = 'python' print('i love %r'%(s1)) #i love 'python'
print():源碼分析:print(self, *args, sep=' ', end='\n', file=None,flush=False):
file: 默認是輸出到屏幕,若是設置爲文件句柄,輸出到文件
sep: 打印多個值之間的分隔符,默認爲空格
end: 每一次打印的結尾,默認爲換行符
flush: 當即把內容輸出到流文件,不做緩存
print(1,2,3) #1 2 3 print(1,2,3,sep='@') # 1@2@3 print(1,2,3,end='') #不換行
all():可迭代對象中,全爲True纔是True。
any():可迭代對象中,有一True即爲True。
zip(): 拉鍊方法;函數用於將可迭代的對象做爲參數,將對象中對應的元素打包成一個個元組,而後返回由這些元祖組成的內容,若是各個迭代器的元素個數不一致,則按照長度最短的返回。
lst1 = [1,2,3] lst2 = ['a','b','c','d'] lst3 = (11,12,13,14,15) for i in zip(lst1,lst2,lst3): print(i) #(1, 'a', 11) (2, 'b', 12) (3, 'c', 13)
min():求最小值,能夠與函數結合(key:自動將可迭代對象中的每一個元素按照順序傳入key對應的函數中,而後以返回值比較大小,key=必定是函數名,不能加(),。min/max默認會按照字典的鍵去比較大小,但能夠本身規定。
#以絕對值的方式取最小值 l1 = [1,2,-1,-5,3] def func(s): return abs(s) print(min(l1,key=func)) #1 #也能夠直接使用匿名函數: print(min(l1,key=lambda s:abs(s))) #1 dic = {'a':3,'b':2,'c':1} min(dic) #a 默認以鍵排序,返回鍵 #以值比較: print(min(dic,key=lambda s:dic[s])) #c 以值排序,返回鍵(返回的是循環的元素,而不是經函數轉換後比較的值。
max():求最大值,能夠與函數結合,同min()。
sorted():排序函數;語法: sorted(iterable,key=None,reverse=False)。key: 排序規則(排序函數),返回列表。在sorted內部會將可迭代對象中的每個元素傳遞給這個函數的參數.根據函數運算的結果進行排序reverse: 是不是倒敘,True:倒敘;False:正序
lst = [1,3,2,5,4] lst2 = sorted(lst) print(lst) # 原列表不會改變 print(lst2) # 返回的新列表是通過排序的 lst3 = sorted(lst,reverse=True) print(lst3) # 倒敘 #字典使用sorted排序 dic = {1: 'a',3: 'c',2: 'b'} print(sorted(dic)) #[1,2,3] 字典排序返回的就是排序後的key #和函數組合使用 # 定義一個列表,而後根據一元素的長度排序 lst = ['1','111','11','1111'] # 計算字符串的長度 def func(s): return len(s) print(sorted(lst,key=func)) #['1','11','111','1111'] lst = [{'id': 1,'name': 'a','age': 18}, {'id': 2,'name': 'b','age': 17}, {'id': 3,'name': '3','age': 16},] # 按照年齡對學生信息進行排序 print(sorted(lst,key=lambda e: e['age']))
filter():篩選過濾,返回一個迭代器(可與列表推導式的篩選模式進行對比);語法: filter(function,iterable),function: 用來篩選的函數,在filter中會自動的把iterable中的元素傳遞給function,而後根據function返回的True或者False來判斷是否保留此項數據
lst = [{'id':1,'name':'a','age':18}, {'id':2,'name':'b','age':17}, {'id':3,'name':'c','age':16},] ls = filter(lambda e:e['age'] > 16,lst) print(list(ls)) #[{'id': 1, 'name': 'alex', 'age': 18},{'id': 1, 'name': 'wusir', 'age': 17}]
map():映射函數,返回一個迭代器(可與列表推導式的循環模式對比);語法: map(function,iterable) 能夠對可迭代對象中的每個元素進映射,分別取執行function。
#例1;計算列表中每一個元素的平方,返回新列表 lst = [1,2,3,4,5] print(map(lambda s:s*s,lst)) #<map object at 0x000002B2A4F04B88> print(list(map(lambda s:s*s,lst))) #例2;計算兩個列表中相同位置的數據的和 lst1 = [1, 2, 3, 4, 5] lst2 = [2, 4, 6, 8, 10] print(list(map(lambda x, y: x+y, lst1, lst2)))
reduce():reduce(函數名,可迭代對象) # 這兩個參數必須都要有,缺一個不行,在Python2.x版本中recude是直接 import就能夠的, Python3.x版本中須要從functools這個包中導入。
from functools import reduce def func(x,y): return x + y ret = reduce(func,[1,2,3]) print(ret) # 結果 6 #reduce的做用是先把列表中的前倆個元素取出計算出一個值而後臨時保存着,接下來用這個臨時保存的值和列表中第三個元素進行計算,求出一個新的值將最開始臨時保存的值覆蓋掉,而後在用這個新的臨時值和列表中第四個元素計算.依次類推。注意:咱們放進去的可迭代對象沒有更改 #如今有[1,2,3,4]想讓列表中的數變成1234 l = reduce(lambda x,y:x*10+y,[1,2,3,4]) print(l)
閉包、裝飾器
全局變量,數據不安全。使用局部變量,保證數據的安全。當內層函數對外層函數非全局變量的引用(使用)時,就會造成閉包。被引用的非全局變量也稱做自由變量,這個自由變量會與內層函數產生一個綁定關係,令自由變量不會再內存中消失。閉包現象只能存在函數的嵌套中。
判斷一個函數有沒有自由變量:
def func1(): l1= [] def func2(num): l1.append(num) return l1 return func2 a = func1() # 函數名.__code__.co_freevars 查看函數的自由變量 print(a.__code__.co_freevars) # ('l1',) # 函數名.__code__.co_varnames 查看函數的局部變量 print(a.__code__.co_varnames) # ('num',) # 函數名.__closure__ 獲取具體的自由變量對象,也就是cell對象。 print(a.__closure__) #(<cell at 0x000001EE151AC738: list object at 0x000001EE135851C8>,) # cell_contents 自由變量具體的值 print(a.__closure__[0].cell_contents) # []
裝飾器:徹底遵循開放封閉原則,即在不改變原函數的代碼以及調用方式的前提下,爲其增長新的功能。(裝飾器的本質是閉包)python提出了一個‘語法糖’的概念。在編寫了裝飾器後,須要將裝飾器代碼放在全部代碼的前方。當要對裝飾器函數調用時,在要裝飾函數的前方使用@+裝飾器函數名
標準版的裝飾器(不帶參數):
def wrapper(f): def inner(*arg,**kargs): '''添加額外的功能:執行被裝飾函數以前的操做''' ret = f(*arg,**kargs) '''添加額外的功能:執行被裝飾函數以後的操做''' return ret return inner #裝飾器的調用 @wrapper def func(): pass
帶參數的裝飾器
閉包原理,內部函數可以使用外部函數的參數。
def logger(*arg,**kargs): #參數能夠本身設定 def wrapper(f): def inner(*arg,**kargs): '''添加額外的功能:執行被裝飾函數以前的操做,並可使用傳進來的path參數''' ret = f(*arg,**kargs) '''添加額外的功能:執行被裝飾函數以後的操做,並可使用傳進來的path參數''' return ret return inner return wrapper @loger('傳參') #wrapper = loger(),func=wrapper(func()) def func(): pass
自定義模塊
模塊的本質就.py文件,封裝語句的最小單位。模塊的分類:內置模塊(200種左右)、第三方模塊(6000多種)、自定義模塊。當python腳本/模塊運行時,python解釋器會自動將一些模塊、函數加載到內存。可用import sys print(sys.modules)
查看
自定義模塊:
-
編寫自定義模塊開頭最好用多行註釋說明模塊內容。
-
模塊中出現的變量、函數定義、類等稱爲模塊的成員。
-
模塊(.py文件)的運行方式:
- 腳本方式:直接用解釋器執行。
- 模塊方式(導入方式):被其餘的模塊導入,爲導入它的模塊提供資源(變量、函數定義、類定義等)。
-
模塊被其餘模塊導入時,會將模塊中的全部代碼加載到內存,其中的可執行語句被會當即執行。所以爲了方便調試和使用,可用
__name__
屬性。將print(__name__)
編寫在模塊中時,以腳本方式運行打印的是字符串__main__
,以被導入的方式運行時,會打印出模塊名。所以模塊應包含if __name__=='__main__':
以用來判斷是腳本方式運行仍是導入方式,此時當模塊被導入時可執行語句不會直接運行。def func(): ...... def main(): pass if __name__=='__main__': main()
導入模塊的方式:
-
import xxx
或import xxx,yyy,....
導入一個模塊或導入多個模塊。常常將import os,sys
這兩個模塊放在一塊導入。可使用別名(alias,縮寫爲as)導入:import xxx as z
導入xxx模塊重命名爲z。當運行至此導入模塊方法代碼時會在內存全局命稱空間中開闢以模塊名命名的新的名稱空間,並將模塊中的全部成員加載至命稱空間中。被導入的每一個模塊都有獨立的命稱空間。
-
from xxx import y
或from xxx import a,b,c,......
:從某個模塊中導入指定的成員或導入多個成員。from xxx import yyy as z
導入xxx模塊將成員名重命名爲z。當運行至此導入模塊方法代碼時會在腳本的全局命稱空間中建立導入的成員的變量,所以不用使用
模塊名.成員名
調用,但用此方法可能會致使命名衝突,後者將前者覆蓋。導入後內存全局命稱空間中開闢以模塊名命名的新的名稱空間,變量的內存地址的指向關係仍然不會改變,每一個模塊仍都有獨立的命稱空間。 -
from xxx import *
:默認從某個模塊中導入所有可導入的成員。配合__all__
使用,被引用的模塊中編寫__all__=['','',...]
列表中是可被導入的成員。但__all__
只對此種導入方法起做用,用於表示模塊能夠被外界使用的成員,元素是成員名組成的字符串,默認不寫是能夠導入所有成員。
系統導入模塊的搜索路徑,以import time
示例:先在內存中查看是否有以該模塊名命名的命稱空間,若是以前成功導入過某個模塊,會直接使用已經存在的模塊。若沒有則再內置模塊路徑中(在python安裝路徑中lib和site-packages文件夾下)尋找。若仍沒有,就會在sys.path
中尋找。
sys.path:查看sys.path內容,用cmd運行:import sys print(sys.path)
打印出的列表第一個元素是當前執行腳本的路徑。列表動態可修改所以可將自定義模塊文件夾所在的絕對路徑添加到sys.path中以便導入:
import sys sys.path.append('模塊文件夾路徑')
在不一樣的目錄下相對導入自定義模塊:
相對導入:針對某個項目中的不一樣模塊之間進行導入,稱爲相對導入(模塊間必須有一個同一個文件夾)。相對導入只有一個語法:from 相對路徑 import xxx
相對路徑:包含了點號的一個相對路徑。
.:表示當前的路徑。
..:表示的是父路徑
...:表示的是父路徑的父路徑
..x.y:表示的是父路徑下x文件夾下的y文件(夾)
舉例:在python文件夾下有t1和t2兩個文件夾,t1和t2文件夾下分別有pt1.py和pt2.py兩個模塊。python文件夾在WorkSpace文件夾下,WorkSpace下有一個main.py文件。
#相對導入(在pt1.py文件中編寫): #1.將pt1做爲對外界的接入口: from ..t2 import pt2 #..:從當前的路徑(不包含當前文件,即/WorkSpace/python/t1)的父目錄(/WorkSpace/python/)下找t2,在從t2中找pt2。 #測試相對導入(在main.py文件中編寫): import os,sys sys.path.append(os.path.dirname(__file__)) #把項目所在的父路徑加到sys.path中。 from python.t1 import pt1 #使用pt1.py模塊文件導入的pt2.py模塊中的成員: pt1.pt2.成員名 #但不推薦這樣寫。容易向外界暴露pt2模塊。 #更改方法(真正的相對導入): #相對導入(在pt1.py文件中編寫): #1.將pt1做爲對外界的接入口: from ..t2.pt2 import * #直接導入pt2模塊 #測試相對導入(在main.py文件中編寫): import os,sys sys.path.append(os.path.dirname(__file__)) #把項目所在的父路徑加到sys.path中。 from python.t1 import pt1 #使用pt1.py模塊文件導入的pt2.py模塊中的成員: pt1.成員名 #成員名:能夠是pt1和pt2模塊的成員。 #不用相對導入(不推薦): #1.在pt1模塊下編寫: import os,sys sys.path.append(as.path.dirname(os.path.dirname(__file__))+'/t2') from pt2 import *
經常使用模塊
使用導入的模塊是否要在其後加():若是是一個類(class)或者是一個函數(function)就要加(),若是是一個屬性就不要加括號。
random模塊
pseudo-random,僞隨機數;提供了隨機數獲取的方法
- random.random():獲取[0.0,1.0)範圍內的浮點數。
- random.randint(a,b):獲取[a,b]範圍內的一個整數。
- random.uniform(a,b):獲取[a,b]範圍內的一個浮點數數。
- random.shuffle(x):把參數指定的數據中的元素打亂,參數必須是一個可變的數據類型。沒有返回值(由於是直接將x打亂,調用改函數後x會被改變,與append無返回值緣由相同。)(不支持元祖,可用sample打亂)
- random.sample(x,k):從x中隨機抽取k個數據,組成一個列表返回。
- random.randrange(start,stop,step):生成一個[start,stop)之間以step爲步長的隨機整數。默認步長爲1。
- random.seek(a):設置初始化隨機數種子。a,隨機數種子,能夠是整數或浮點數。使用random庫產生隨機數不必定要設置隨機數種子,若是不設置,random庫默認以系統時間產生當作隨機數種子。設置隨機數種子的好處是能夠重複在現相同的隨機數序列。
l1 = [1,2,3,4,5,6,] #列表是有序的 s1 = {1,2,3,4,5,6,} s2 = 'wfwhgfwqnbvc阿爾天花板vc' t1 = (1,2,3,4,5,6,) import random random.seed() print(random.random()) print(random.uniform(1,8)) print(random.randint(2,8)) print(random.shuffle(l1)) #只能用於有下標類型,而且能夠用x[i],x[j]=x[j],x[i]互換,dict、set、tuple不行。 print(l1) random.shuffle(s2) print(s2) s3 = random.sample(t1,len(t1)) #取樣, print(t1)
time模塊
封裝了獲取時間戳和時間戳與字符串形式的一些方法,時間戳:從時間元年(1970年,1月,1日,00:00:00)到如今通過的秒數(毫秒數,不一樣系統/編程語言不同。)
import time # 獲取時間戳: print(time.time()) # 默認使用當前時間戳, # 獲取格式化時間對象(方法:gmtime()、localtime()),格式化時間對象:struct_time: print(time.gmtime()) #接受參數爲時間戳,默認不寫使用當前時間戳,獲取格林威治(GMT)時間。 print(time.localtime()) #接受參數爲時間戳,默認不寫使用當前時間戳,獲取當地時間,tm_hour與格林威治時間(時區差)不一樣。 # 格式化時間對象和時間字符串(str)之間的轉換。time.strftime(format,str) a = time.localtime() print(time.strftime('%Y-%m-%d %H:%M:%S',a)) #接受兩個參數,第一個是時間格式,第二個是格式化時間對象(默認不寫用當前的時間戳的格式化時間對象)。 # 時間字符串轉換爲時間對象(time.strptime(str,format)): print(time.strptime('2000 1 1','%Y %m %d')) #不指定時間用默認值(小時分鐘等用0,月份天等用1) # 格式化時間對象轉換爲時間戳:time.mktime() print(time.mktime(time.localtime())) # 暫停當前程序:time.sleep() 參數爲秒 time.sleep(2) #暫停2秒
datetime模塊
封裝了一些日期和時間相關的類。主要用於數學計算
date類(年月日):
#date類 d = datetime.date(2010,10,10) print(d) #2010-10-10 print(d.year) #2010 print(d.month) #10 print(d.day) #10
time類(時分秒):
#time類 t = datetime.time(10,11,12) print(t) print(t.hour) print(t.minute) print(t.second)
datetime類
dt = datetime.datetime(2010,10,10,11,11,11) print(dt) print(dt.year)
timedelta類: 參與數學運算,建立時間對象(只能和date,datetime,timedelta進行運算),會產生進位,和時間段進行運算的結果類型和另外一個被操做的類型相同。
td = datetime.timedelta(seconds=3) d = datetime.datetime(2000,10,10,10,10,59) res = d+td print(res)
練習:隨便給出一年,計算2月份有多少天。
import datetime #1.首先建立出指定年份的3月1號,而後讓它往前走一天。 year = int(input("輸入年份:")) #2. 建立指定年份的date對象。 d = datetime.date(year,3,1) dt = datetime.timedelta(days=1) res = d-dt #和時間段進行運算的結果類型和前方被減類型相同。 print(res.day)
os模塊
和操做系統相關的操做被封裝在這個模塊中。
和文件相關的:重命名、刪除、
import os #刪除文件 os.remove() #參數爲文件的路徑 #重命名文件 os.rename() #兩個參數,第一個爲文件路徑,第二個爲文件新名 #刪除目錄,必須是空目錄,在程序中刪除不會放在回收站中。 os.removedirs() #一個參數,目錄的路徑 #使用shutil模塊能夠刪除帶內容的目錄。 import shutile shutile.rmtree() #一個參數,文件夾目錄 os.getcwd() #可查看當前的目錄 os.chdir(‘指定目錄’) #可切換目錄
和路徑相關的操做,被封裝到另外一個子模塊中:os.path
絕對路徑:從盤符開始定位的路徑;相對路徑:從當前文件定位的路徑
exists(path) 判斷路徑是否存在 若文件不存在則爲False isdir(s) 判斷是否爲一個目錄 若文件不存在則爲False isfile() 判斷是不是一個文件 若文件不存在則爲False 如下方法不會判讀文件是否存在 dirname(path) 取一個路徑前的目錄(父目錄),不會判斷目錄是否存在, basename(path) 取一個路徑中最後的部分 split(path) 分割路徑,返回一個二元祖,第一個元素爲路徑前的(父目錄),第二個爲路徑中最後的部分。 join(path,paths) 合併路徑。 abspath(path) 將路徑變爲絕對路徑 isabs() 判斷一個路徑是否爲絕對路徑
import os res = os.path.dirname(r'd:/aaa/bbb/ccc/text.py') print(res) #d:/aaa/bbb/ccc res = os.path.basename(r'd:/aaa/bbb/ccc/text.py') print(res) #text.py res = os.path.join('d:\\','aaa','bbb','cc') #有一個轉義字符 print(res) #d:\aaa\bbb\cc res = os.path.abspath(r'd:\aaa\bbb\cc') print(res) #D:\aaa\bbb\cc res = os.path.abspath(r'\aaa\bbb\cc') #若是是\開頭的路徑,默認是在當前盤符下。 print(res) #D:\aaa\bbb\cc res = os.path.abspath(r'aaa\bbb\cc') #若是不是\開頭,則是當前腳本的路徑。 print(res) #D:\WorkSpace\Python\text1\aaa\bbb\cc
使用os模塊獲取一個文件路徑的父路徑:import os os.path.dirname(__file__)
,__file__
:獲取模塊的路徑。import os os.__file__
sys模塊
提供瞭解釋器使用和維護的變量和函數;沒有提供源碼,用c語言編寫直接集成在解釋器上;默認會導入os模塊,但若要使用os模塊仍需import os
。
sys.argv
:以當前腳本方式執行程序時,從命令行獲取參數。
argv[0]
表示的是當前正在執行的腳本名,argv[1]
表示第一個參數,以此類推。
import sys print("腳本名:",sys.argv[0]) print("第一個參數:",sys.argv[1]) #在命令行運行,傳入兩個參數,會將兩個參數打印出來
sys.path
系統尋找模塊的路徑,能夠經過PYTHONPATH來進行優化。因爲是程序執行的時候進行初始化的,因此路徑的第一項path[0]始終是調用解釋器的腳本所在的路徑,若是是動態調用的腳本,或者是從標準輸入讀取到的腳本命令,則path[0]是一個空字符串,程序中能夠隨時對這個路徑進行修改,已達到動態添加模塊路徑的目的。
sys.modules
:以字典形式返回系統已經加載的模塊。常做爲是否從新從新加載模塊的判斷。
json模塊
將數據結構直接轉換爲字符串用str()
能夠,但再將字符串數據轉換爲原先的數據結構就會出現問題。
l1 = ['ab','cd'] s1 = str(l1) print(s1) #['ab', 'cd'] print(list(s1)) #['[', "'", 'a', 'b', "'", ',', ' ', "'", 'c', 'd', "'", ']']
JavaScript Object Notation:Java腳本兌現標記語言。已經成爲一種簡單的數據交換格式。將數據轉換成特殊字符串,不一樣語言都遵循的一種數據轉化格式,用於儲存dump()和load()或dumps()和loads()
或網絡傳輸dumps()和loads()
。
json序列化:
import json s = json.dumps([1,2,3,4,]) #將指定的對象轉換成json格式的字符串,仍然放在內存中 print(s,type(s)) #[1, 2, 3, 4] <class 'str'> #元祖序列化後變成列表 import json s = json.dumps((1,2,3,4,)) print(s,type(s)) #[1, 2, 3, 4] <class 'str'> #集合不可一被json序列化 #將結果直接寫到文件中:json.dump(obj,fb) with open('a.txt',mode='at',encoding='utf-8') as f: json.dump(['1,2,3,'],f)
json反序列化:
import json res = json.dumps([1,2,3,]) lst = json.loads(res) #從文件中反序列化:json.loads(fb) with open('a.txt',encoding='utf-8') as f: res = json.load(f)
dump()和load()
只能一次性寫,一次性讀。把須要序列化的對象,經過dumps()和loads()
的方式,可實現屢次讀或寫,寫入時要換行,才能讀出再轉化。
with open('a.txt',mode='wt',encoding='utf-8') as f: for i in range(10): f.write(json.dumps()+'\n') with open('a.txt',encoding='utf-8') as f: for i in f: json.loads(i.strip()) # 或json.loads(i),默認去除換行
pickle模塊
可將python中全部的數據類型轉換成字節串
元祖在pickle中轉換不會改變類型。
pickle也能夠轉換set類型
pickle中dump()和load()
能夠屢次讀寫一個文件。dump(s,f)
能夠寫入python中的任何對象(變量、函數、類等)
import pickle bys = pickle.dumps((1,2,3)) #轉化爲字節 res = pickle.loads(bys) print(type(res),res) #<class 'tuple'> (1, 2, 3) #把pickle序列化內容寫入文件中: with open('b.txt',mode='wb') as f: pickle.dump([1,2,3],f) #從文件中反序列化pickle數據: with open('b.txt',mode='rb') as f: pickle.load(f)
pickle與json的比較
json:
- 不是全部的數據類型均可以序列化
- 不能屢次對同一個文件序列化
- json數據能夠跨語言。
- json序列化只支持部分python數據結構:dict、tuple、int、float、True、False、None
pickle:
- 全部的python類型都能序列化,結果爲字節串。
- 能夠屢次對同一個文件序列化
- 不能跨語言,只能在python中使用。
hashlib模塊
封裝一些用於加密的類。加密的目的:用於判斷和驗證,而非解密。給一個數據進行加密,用另個加密的結果和第一次加密的結果進行對比,若是加密結果想同,說明原文相同。
特色:
- 把一個大的數據切分紅不一樣的塊,分別對不一樣的塊進行加密,再彙總的結果和直接對總體數據加密的結果是一致的。
- 單向加密,不可逆。
- 原始數據的一點小的變化,將致使加密結果很是大的差別。
md5加密算法:
import hashlib #獲取一個加密對象 m = hashlib.md5() #使用加密對象的update進行加密 m.update(b'abc') #將abc變爲字節,如有中文則爲'abc'.encode('utf-8') #經過hexdigest()獲取加密。 res = m.hexdigest() print(res) #900150983cd24fb0d6963f7d28e17f72
給一個數據加密的步驟:
-
獲取一個加密對象,不僅是有md5,還有sha系列
sha224', 'sha256', 'sha384', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'sha512'
等,隨着sha系列數字越高,加密越複雜,。在建立加密對象時,能夠指定參數,使加密程度更高。import hashlib m = hashlib.md5('abc'.encode('utf-8')) m.update('qaz'.encode('utf-8')) print(m.hexdigest())
-
使用加密對象的
update()
方法進行加密。加密對象能夠調用屢次(即爲把一個大數據切分紅小數據,再把小數據進行累次加密,不是對同一個數據屢次加密)。都是對字節進加密。相同的加密對象將字節轉化成固定長度的字符串。 -
一般經過
hexdigest()
獲取加密字符串,也能夠經過digest()
獲取加密字節串。不一樣加密算法的加密結果長度不一樣,隨着加密結果的變長,加密時間也會變長。
#註冊和登陸程序 import hashlib def register(): username = input('輸入用戶名:') passwd = input("輸入密碼:") get_passwd = get_md5(passwd) #加密 with open('login',mode='at',encoding='utf-8') as f: #寫入文件 f.write(f'{username}|{get_passwd}\n') def login(): username = input('輸入用戶名:') passwd = input('輸入密碼:') get_passwd = get_md5(passwd) res = f'{username}|{get_passwd}\n' with open('login',mode='rt',encoding='utf-8') as f: for line in f: if res == line: return True else:return False def get_md5(passwd): m = hashlib.md5('12') #再次加密 m.update(passwd.encode('utf-8')) return m.hexdigest() while True: op = input('1.註冊 2.登陸 3.退出') if op == '1': register() elif op == '2': res = login() if res: print('登陸成功') else: print('登陸失敗,請從新登陸') elif op == '3': break else:print("您輸入的有誤,請從新輸入")
文件的校驗:
Linux中一切皆文件:文本文件,非文本文件,音頻,視頻,圖片……。不管下載的視頻仍是國外的軟件每每都會有一個md5值。
collections模塊
封裝了一些經常使用的容器類。
namedtuple()
:命名元祖,可使用屬性的方式引用元祖中的數據。
import collections as co: #namedtuple()的返回值是一個類 Rectangle = co.namedtuple('Rectangle_class',['length','width']) #第一個參數是對類名的說明信息,不能有空格。namedtuple()的返回值是賦值給了自定的類名。 r = Rectangle(10,5) #經過屬性訪問元祖的元素 print(r.length) print(r.width) #經過引所的方式訪問元素 print(r[0])
defaultdict()
:默認值字典
#defaultdict():(自定義)函數(不能有參數)充當第一個參數 d = co.defaultdict(lambda :'hello',name='aa',age='10') print(d['na']) #hello 若無要查詢的鍵,不會報錯,會根據函數返回必定的值,且會自動將此鍵值增長至字典中 print(d) #defaultdict(<function <lambda> at 0x000001F90D30D378>, {'name': 'aa', 'age': '10', 'na': 'hello'})
Counter()
:計數器,返回值是字典,必須使用可哈希的數據。
c = co.Counter('asasasasaszszxaxzsxazsx') print(c) #Counter({'s': 8, 'a': 7, 'z': 4, 'x': 4}) print(c.most_common(3)) #[('s', 8), ('a', 7), ('z', 4)]
re模塊
findall()
:返回全部符合匹配結果的列表,只有一個元祖元素,每一個分組是元祖中的每一個元素。若是正則進行分組,則只顯示分組裏的內容。若不想讓每一個分組返回;取消分組優先,在分組括號中增長?:
search()
:返回一個對象(變量),需和group()配合。若是進行分組,則只顯示第一個分組裏的內容,但能夠經過group(n)
獲取第n個分組的內容(n>0),默認爲0,返回全部的分組結果。
設置參數flags=re.S
可以讓.
匹配任意內容,包括換行符。
import re ret = re.search('(d+)\w','123123adas1213as') if ret: #若ret爲None直接打印ret.group()就會報錯。 print(ret.group())
split()
:能夠根據正則表達式切割字符串,返回分組,若給正則加上分組,則會將分組中匹配的內容保留至列表中。
sub()
:替換。re.sub('\d','a','123qwe21',1)
將數字替換成a,只替換1次。
subn()
:替換,返回二元祖。第一個爲替換結果,第二個爲替換次數。
match()
:只匹配開頭,經過group()
取值,至關於使用search()
方法在正則表達前使用^
。用來規定字符串必須是什麼。
時間:完成一個程序所須要的代碼行數;在執行代碼的過程當中,底層程序是如何工做的。
空間:佔用的內存條資源,程序的執行效率。
compile()
:假如同一個正則表達式要被屢次使用,可用re.compile('正則表達式')
可節省了屢次解析同一個正則表達式的時間。從時間上提升了效率。若是不重複使用正則,則不會節省時間。
finditer()
:將正則表達式的匹配全部的結果變成一個迭代器,用.group()
每一個獲取結果。節省了空間。
import re ret = re.compile('\d') res = ret.finditer('qaq112qsx123sxa') for i in res: print(r.group())
分組命名:在分組中取名?P<分組名>
將名字放在<>
之間。能夠用group('分組名')
獲取。分組匹配的內容能夠被引用。
import re exp = '<h1>123qdwdcasd</h1>' ret = re.search('<(?P<tag>\w+)>.*?</(?P=tag)>',exp) #第二個分組引用的是第一個分組匹配的內容。 print(ret)
分組間也能夠用\n
引用:n爲要引用的第幾個分組。
import re exp = '<h1>123qdwdcasd</h1>' ret = re.search(r'<(?P<tag>\w+)>.*?</\1>',exp) #在python中`\一、\二、\3等是有特殊意義的,所以要表示在正則中的意義須要轉義,即加r或\ ret = re.search('<(?P<tag>\w+)>.*?</\\1>',exp)
shutil模塊
import shutil # 拷貝文件,shutil.copy2('原文件', '現文件') shutil.copy2('file', 'temp') # 拷貝目錄,shutil.copytree("原目錄", "新目錄",ignore=shutil.ignore_patterns(*args)) #ignore,忽略要copy的文件 shutil.copytree("/Users/jingliyang/PycharmProjects", "logging模塊2", ignore=shutil.ignore_patterns('*.py')) #*.py:全部的以.py結尾的。 # 刪除目錄,shutil.rmtree("temp", ignore_errors=True) ignore_errors:是否忽略一些錯誤。 shutil.rmtree("logging模塊2", ignore_errors=True) # 移動文件/目錄,並能夠重命名。 shutil.move("logging模塊", "logging2", copy_function=shutil.copy2) # 獲取磁盤使用空間 total, used, free = shutil.disk_usage(".") .:查看當前磁盤的空間,也能夠用:'c:\\' print("當前磁盤共: %iGB, 已使用: %iGB, 剩餘: %iGB"%(total / 1073741824, used / 1073741824, free / 1073741824)) # 壓縮文件,shutil.make_archive('壓縮文件夾的名字', 'zip','待壓縮的文件夾路徑') shutil.make_archive('logging2', 'zip','/Users/jingliyang/PycharmProjects/面試題/經常使用模塊/隨機數') # 解壓文件,shutil.unpack_archive('zip文件的路徑.zip','解壓到目的文件夾路徑') shutil.unpack_archive('/Users/jingliyang/PycharmProjects/面試題/經常使用模塊/shutil模塊/logging2.zip','/Users/jingliyang/PycharmProjects/面試題/經常使用模塊/shutil模塊/tmp')
logging模塊
log:用來記錄用戶的行爲,以便數據分析,操做審計;用來排查代碼中的錯誤。
基本配置:
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
默認狀況下Python的logging模塊將日誌打印到了標準輸出中,且只顯示了大於等於WARNING級別的日誌,這說明默認的日誌級別設置爲WARNING(日誌級別等級CRITICAL > ERROR > WARNING > INFO > DEBUG),默認的日誌格式爲日誌級別:Logger名稱:用戶輸出消息。
靈活配置日誌級別,日誌格式,輸出位置:
import logging file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',) logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', handlers=[file_handler,], level=logging.ERROR ) logging.error('你好')
日誌切割
import time import logging from logging import handlers sh = logging.StreamHandler() #同時輸出到屏幕 rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024,backupCount=5) #maxBytes:按照文件的大小切割,backupCount最多保留的文件數 fh = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=5, encoding='utf-8') #when='s'按時間切割,默認爲小時。interval=5每5秒切割一次。 logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s[%(lineno)d] -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', handlers=[fh,sh,rh], level=logging.ERROR ) for i in range(1,100000): time.sleep(1) logging.error('KeyboardInterrupt error %s'%str(i))
配置參數:
logging.basicConfig()函數中可經過具體參數來更改logging模塊默認行爲,可用參數有: filename:用指定的文件名建立FiledHandler,這樣日誌會被存儲在指定的文件中. filemode=:文件打開方式,在指定了filename時使用這個參數,默認值爲「a」還可指定爲「w」(filemode='w')。 format:指定handler使用的日誌顯示格式。 datefmt:指定日期時間格式。 level:設置rootlogger的日誌級別 stream:用指定的stream建立StreamHandler。能夠指定輸出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默認爲sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。 format參數中可能用到的格式化串: %(name)s Logger(用戶,管理員爲root)的名字 %(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用戶輸出的消息
logger對象配置
import logging logger = logging.getLogger() # 建立一個handler,用於寫入日誌文件 fh = logging.FileHandler('test.log',encoding='utf-8') # 再建立一個handler,用於輸出到控制檯 ch = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setLevel(logging.DEBUG) fh.setFormatter(formatter) ch.setFormatter(formatter) logger.addHandler(fh) #logger對象能夠添加多個fh和ch對象 logger.addHandler(ch) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')
logging庫提供了多個組件:Logger、Handler、Filter、Formatter。Logger對象提供應用程序可直接使用的接口,Handler發送日誌到適當的目的地,Filter提供了過濾日誌信息的方法,Formatter指定日誌顯示格式。另外,能夠經過:logger.setLevel(logging.Debug)設置級別,固然,也能夠經過fh.setLevel(logging.Debug)單對文件流設置某個級別。
第二章 面向對象
待續….