目錄
- 第一章 python基礎
- 初識
- *的魔術用法:
- 格式化輸出(3.6版本之后)
- 編碼
- 資料型別
- 序列化
- 軟體開發規范
- str
- list
- tuple
- 字典
- set
- 正則運算式
- 代碼塊、快取機制
- 深淺copy
- 檔案的操作
- 函式
- 命稱空間
- 迭代器
- 生成器
- 遞回函式
- 內置函式
- 閉包、裝飾器
- 帶引數的裝飾器
- 自定義模塊
- 常用模塊
- random模塊
- time模塊
- datetime模塊
- os模塊
- sys模塊
- json模塊
- pickle模塊
- hashlib模塊
- collections模塊
- re模塊
- shutil模塊
- logging模塊
- 初識
- 第二章 面向物件
第一章 python基礎
初識
-
cpu 記憶體 硬碟 作業系統
cpu:計算機的運算和計算中心,相當于人類大腦,
? 記憶體:暫時存盤資料,臨時加載資料應用程式,運行速度快,高鐵,斷電即消失,造價很高,
? 硬碟:磁盤,長期存盤資料,
? 作業系統:一個軟體,連接計算機的硬體與所有軟體之間的一個軟體, -
python的編程語言分類
-
編譯型:將代碼一次性全部編譯成二進制,然后再執行,優點:執行效率高,缺點:開發效率低,不能跨平臺,代表語言:C
-
解釋型:逐行解釋成二進制,逐行運行,優點:開發效率高,可以跨平臺,缺點:執行效率低,代表語言:python,
-
-
python的種類
- Cpython:官方推薦解釋器,可以轉化成C語言能識別的位元組碼,
- Jpython: 可以轉化成Java語言能識別的位元組碼,
- Ironpython:可以轉化成.net語言能識別的位元組碼
- pypy: 動態編譯,
......
-
變數:只是指向某種資料
-
常量:python中沒有真正的常量,為了應和其他語言的口味,全部大寫的變數稱之為常量,
-
while/for 的else:如果回圈沒有被break終止則else陳述句會被執行,如果while被break終止,則不執行else陳述句,
-
運算子:算數運算子+、-等;比較運算子>、==等;賦值運算子=、+=等;邏輯運算子not、and、or;成員運算子in、not in,
and or not- 在沒有()的情況下,優先級: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-8、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中`\1、\2、\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)單對檔案流設定某個級別,
第二章 面向物件
待續….
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/160529.html
標籤:Python
上一篇:python基礎 — 編碼
