python字典的鍵值對輸出次序問題:
在2.7-3.5的python版本中,字典的鍵值對是按照哈希表的存儲順序排列輸出的,
而在3.6及以上版本中, 字典的鍵值對是按照初始化時的排列順序輸出的。
python 中 可變類型 和 不可變類型:
將python中的類型分為 兩個大類,可變 和 不可變,
可變:list dict set
不可變:str,int(float),bool,tuple
python 中的緩存 和 小數據池:
小數據池 指的是 不同代碼塊下的 緩存機制,只是 縮小了 數字 的范圍, 和 少的字符串, 都是為了提升性能,
python 中 深淺copy:
對於一個 嵌套的列表,
淺copy 是只新 copy 一個列表外殼,
深copy 除了copy 一個新的列表外殼,內部的也會copy新的內存,
1 def func(): 2 #1, 賦值 不 創建新內存空間 3 ''' 4 a = [1,2,3 ,[4,5,6]] 5 b = a 6 print(id(a),id(b)) 7 print(id(a[-1]),id(b[-1])) 8 ''' 9 10 # copy 指的就是要創建新的 內存空間 11 #2,淺copy 12 ''' 13 c = [1, 2, 3, [4, 5, 6]] 14 d = c.copy() # 這是淺copy 15 print(id(c),id(d)) 16 print(id(c[-1]),id(d[-1])) 17 # 所以淺copy 只是copy 一個外殼 18 ''' 19 20 #3,深copy 創建新的內存空間最多 21 ''' 22 from copy import deepcopy 23 e = [1, 2, 3, [4, 5, 6]] 24 f = deepcopy(e) 25 print(id(e),id(f)) 26 print(id(e[-1]),id(f[-1])) 27 ''' 28 # 所以,深copy 不只是新創建了一個殼子,而且也新創建了 里面的小殼子 29 30 if __name__ == '__main__': 31 func()
注: python 對深copy 也不是一味的開辟新內存,它是有條件的,對於不可變類型是不會 開辟的,例如:str,int ,float ,tuple!
還有,切片操作是 淺copy ,
1 l1 = [1,2,3,[11,22,33]] 2 l2 = l1[:] (淺copy ) 3 l1.append(666) 4 print(l1) 5 print(l2)
Python中 可以給數據 組合加上 索引的enumerate 函數:
enumerate() 函數用於將一個可遍歷的數據對象(如列表、元組或字符串)組合為一個索引序列,同時列出數據和數據下標,一般用在 for 循環當中:
1 def func(): 2 ''' 3 l = ["tom","a","b"] 4 5 ret = enumerate(l) 6 print(ret) # <enumerate object at 0x0371E198> #該對象 可迭代 7 for item in ret: 8 print(item) 9 ''' 10 11 ''' 12 s = "abcdefg" 13 ret = enumerate(s) 14 print(ret) 15 for item in ret: 16 print(item) 17 ''' 18 19 s = "woqu" 20 # 多用於 for in 結構 21 for item in enumerate(s): 22 print(item) 23 24 25 26 27 if __name__ == '__main__': 28 func()
Python中 編碼 Unicode 和 utf-8/gbk 相關:
1 def func(): 2 3 s = "hello 世界" #s 是在內存中,它是Unicode 編碼,(必須要轉為 非unicode 才可以用於 網絡傳輸 或 保存到硬盤 ) 4 print(s,type(s)) 5 6 #Python 中 所有的數據類型里,只有 字節類型(bytes) 的編碼方式 是 非unicode 7 8 b = s.encode() # 默認編碼 utf8 一個漢字 3個字節 # b此時在內存 是utf8 編碼 b數據類型是bytes 9 print(b,type(b)) 10 11 c = s.encode(encoding="gbk") #gbk 一個漢字2個字節 # c 此時在內存中是 gbk編碼 c數據類型是 bytes 12 print(c,type(c)) 13 14 # 如何將c (gbk): hello \xca\xc0\xbd\xe7 轉為 b(utf8) :hello \xe4\xb8\x96\xe7\x95\x8c 呢? 15 # 這要通過 unicode 中間 轉化, 因此需要通過 str 類型 (unicode) 轉化, 16 ''' 17 # d = c.decode(encoding="utf8") # 這肯定不行,c 是以 gbk編碼的,必須以 gbk解碼 18 d = c.decode(encoding="gbk") 19 print(d,type(d)) 20 21 # 然后 再將 d 轉為 utf8 22 e = d.encode(encoding="utf8") 23 print(e,type(e)) 24 25 ''' 26 27 # 總結: 28 # 1,內存中的數據類型 除了bytes 都是 Unicode , 29 # 2,要想保存硬盤 或網絡傳輸 必須要是 非Unicode ( utf8 / gbk), 所以,保存 或 傳輸 要轉為 bytes 類型 30 # 3,非 Unicode 之間 如果想互轉 要通過Unicode 來轉 31 32 if __name__ == '__main__': 33 func()
bytes 的使用:
不過,以后不會直接用 str 中轉 ,因為如果字典 沒辦法直接 變為 str ,和 從str 再變回來, 一般使用json 模塊 的loads() 和 dumps() ,
如下:
1 import json 2 def func(): 3 d = {'name':'a'} 4 5 # 將字典 變為 utf8 用於傳輸 6 ret = json.dumps(d).encode("utf8") 7 print(ret,type(ret) ) 8 9 # loads 直接可以轉為 程序中使用的字典 10 ret2 = json.loads(ret) 11 print(ret2,type(ret2)) 12 13 if __name__ == '__main__': 14 func()
Python 中的 *args 和 **kwargs :
1 def func(a,*args,b=0,**kwargs): #位置參數 和 關鍵字參數 和 args 和 kwargs 的順序 2 print(a) 3 print(b) 4 print(args) 5 print(kwargs) 6 7 if __name__ == '__main__': 8 func(1,2,1,2,b=10,d=1)
注:形參位置 * 和 ** 代表的都是聚合, *代表把位置參數聚合成一個元祖, ** 代表把關鍵字參數聚合成一個 字典 。
實參位置的* 和 ** 代表 的是打散,

1 ''' 2 def func(): 3 a= [1,2,3] 4 print(a) 5 print(*a) # 在實參位置 * 代表打散 6 ''' 7 def func2(a,b,c): 8 print(a) 9 print(b) 10 print(c) 11 12 def func3(name,age): 13 print(name) 14 print(age) 15 16 17 if __name__ == '__main__': 18 # func() 19 20 temp = [1,2,3] 21 func2(*temp) # * 在實參位置 處代表 打散 --> func2(1,2,3) 22 23 temp = {"name":"tom","age":18} 24 func3(**temp) # ** 在實參位置 代表 打散 --> func3(name='tom',age=18) 25 26 # 注: 實參位置要求 只要是可迭代對象 都可以被* 或 ** 打散 27 # 例如 28 ''' 29 temp = "abc" 30 def func4(a,b,c): 31 print(a) 32 print(b) 33 print(c) 34 35 func4(*temp) 36 '''
*的其他用法:
* 除了可以 聚合 和 打散之外,還可以用於 處理剩下的元素:
1 def func(): 2 a,*b = range(1,10) 3 print(a) #1 4 print(b) #[2, 3, 4, 5, 6, 7, 8, 9] 5 6 if __name__ == '__main__': 7 func()
Python 中的 命名空間 :
總共有 三個命名空間: 內置 命名空間, 全局命名空間,局部命名空間,
它的加載順序是 : 內置命名空間 --> 全局命名空間 --> 局部命名空間。
查找變量順序 符合 就近原則 !
Python 中的 作用域:
總共兩個 作用域:
全局作用域: 內置命名空間 和 全局命名空間
局部作用域: 局部命名空間
局部作用域 里 只可以使用(不可修改)全局作用域里的 變量,
此時會報錯,為什么?
因為 當我們在局部作用域中修改一個 變量的時候,解釋器會認為我們已經在局部作用域定義了這個變量,所以報錯,
UnboundLocalError: local variable 'num' referenced before assignment
解決方法:可以使用global 關鍵字 把num 聲明為 全局作用域中 的變量num ,此時就可以更改了。
除了,局部 和 全局, 如果局部中又有 局部,也是如此~
Python 中的 高階函數 :
高階函數:一個函數可以作為參數傳給另外一個函數,或者作為一個函數的返回值,滿足其一則為高階函數。
1 def test(): 2 print("I am test") 3 4 def func(f): 5 print("I am func") 6 f() 7 return f 8 9 if __name__ == '__main__': 10 ret = func(test) # func() 函數的參數為 一個函數, 故func() 為高階函數 11 12 # func() 函數的 返回值 為一個函數,故func() 為高階函數 13 ret()
常見的高階函數: map() reduce() filter() 它們也都是內置函數

1 def add(x): 2 return x+1 3 4 def test(): 5 l = [1, 2, 3, 4, 5] 6 ret = map(add,l) # 因為 內置函數map 的參數 有一個為函數,故為高階函數 7 print(ret) 8 print(list(ret)) 9 10 11 if __name__ == '__main__': 12 test()

1 def isTrue(x): 2 if x>20: 3 return True 4 5 def func(): 6 l = [17,18,19,20,21,22,23] 7 ret = filter(isTrue,l) 8 print(ret) 9 print(list(ret)) 10 11 12 if __name__ == '__main__': 13 func()

1 from functools import reduce # reduce 使用的時候要先導入 2 3 def test(x,y): # 定義 reduce 的方式 4 return 2*x + y 5 6 def func(): 7 l = [17,18,19,20,21,22] 8 ret = reduce(test,l) # 整個過程等同於: 2*(2*(2*(2*(2*17 + 18)+19)+20)+21)+22 9 print(ret) 10 print(2*(2*(2*(2*(2*17 + 18)+19)+20)+21)+22) 11 12 if __name__ == '__main__': 13 func()
注:reduce 使用前要導入
總結: map 和 filter 是迭代的時候 不管前后元素, reduce 迭代的時候 是前后元素一起來的,
Python 中的 globals() 和 locals() 兩個內置函數 :
globals() 返回一個字典:包含全局作用域(內置+全局) 中的所有內容 locals() 返回一個字典:包含當前作用域 中的所有內容
1 name = "tom" 2 age = 18 3 def func(): 4 a = 1 5 b = 'jack' 6 7 print(globals()) 8 print(locals()) 9 10 11 if __name__ == '__main__': 12 print(globals()) 13 print(locals()) 14 func() 15 ''' 16 總結: 17 globals() 返回一個字典:包含全局作用域(內置+全局) 中的所有內容 18 locals() 返回一個字典:包含當前作用域 中的所有內容 19 '''
Python 函數的 默認參數的坑:
默認參數針對的是形參,
1 def func(a,l=[]): # 默認參數的 坑 (只針對 可變數據類型 ) 2 l.append(a) 3 return l 4 5 if __name__ == '__main__': 6 ret = func('tom') 7 print(ret) # ['tom'] 8 ret2 = func('jack') 9 print(ret2) # ['tom', 'jack'] 按理說應該是 ['jack'] 10 #總結: 對於默認參數,如果是可變數據類型,那么為了 提高性能 也是不會重復創建的

1 def func(a,l=[]): 2 l.append(a) 3 return l 4 5 if __name__ == '__main__': 6 ret1 = func(1) 7 print(ret1) 8 ret2 = func(2,[]) 9 print(ret2) 10 ret3 = func(3) 11 print(ret3) 12 # 其中 ret1 和 ret3 是同一個內存地址 ret2 是另一個內存地址
對於 默認參數,如果是可變類型,如果不傳入的話,沿用的還是同一個,如果傳入 的話,是新的一個 。
Python 中的 偏函數:
它的作用是:將函數 和 實參 存起來,執行時一起執行,

from functools import partial # 偏函數 def sumTwo(a,b): return a+b new_sum = partial(sumTwo,1,2) # print(new_sum) print(new_sum())
它在flask 的源碼中可以看到,
Python 中 global 和 nonlocal 關鍵字:
1,global 關鍵字 是在 “局部” 作用域 聲明一個全局變量 。
1 def func(): 2 global num 3 num = 10 4 5 if __name__ == '__main__': 6 func() 7 print(num)
在局部的局部也可用 global 聲明一個全局變量,
1 def func(): 2 def test(): 3 global num 4 num = 10 5 test() 6 7 8 if __name__ == '__main__': 9 func() 10 print(num)
global 關鍵字也可以修改全局變量,
2,nonlocal 關鍵字
(1)不能操作全局變量
(2)它主要是 內層函數 對 外層函數 變量 進行修改
1 def func(): 2 num = 1 3 def a(): 4 def test(): 5 nonlocal num 6 num = 10 7 test() 8 print(num) 9 a() 10 print(num) 11 12 if __name__ == '__main__': 13 func()
Python 中 獲取一個對象的所有方法名字 返回一個列表 dir() 內置函數:
1 l = [1,2,3,4] 2 3 if __name__ == '__main__': 4 print(dir(l)) 5 ''' 6 ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] 7 ''' 8 #因為 __iter__ 在里面,__next__ 不在里面,所以列表對象是 可迭代對象, 但是不是迭代器對象
Python 中 dir() 和 __dict__() 的區別:
在python中__dict__與dir()都可以返回一個對象的屬性,區別在於:
1,__dict__是對象的一個屬性,而dir()是一個內置函數
2,__dict__返回一個對象的屬性名和值,即dict類型,而dir()返回一個屬性名的list;
3,__dict__不是每一個對象都有,而dir()對各種類型對象都起作用,如module, type, class, object;
4,_dict__只返回對象的可寫屬性(writable attributes),而dir()返回各種相關(relavent)屬性,且對於不同類型對象,作用不同。
Python 中 可迭代對象 和 迭代器對象:
可迭代對象: 對象所對應類 的中 有 __iter__() 保留方法,
迭代器對象:對象所對應的類 中有 __iter__() 並且 也有 __next__() 保留方法,
因此,一個對象如果是迭代器對象,那么必是可迭代對象,但是是可迭代對象 不一定是迭代器對象 。
1 from _collections_abc import Iterable,Iterator 2 l = [1,2,3,4] 3 if __name__ == '__main__': 4 print(issubclass(type(l), Iterable)) # Iterable 內部是驗證 是否有 __iter__ 保留方法 5 print(issubclass(type(l), Iterator)) # Iterator 內部是驗證 是否有 __iter__ 和 __next__ 保留方法 6
Iterable 用來檢查是否是 可迭代對象,
Iterator 用來檢查是否是 迭代器對象 ,
可迭代對象 不可以直接遍歷, 迭代器才可以遍歷,
Python 中如何將 可迭代對象 轉化為 迭代器對象:
通過 iter() 內置函數 / .__Iter__() 進行轉換
1 from _collections_abc import Iterator 2 l = [1,2,3,4] 3 4 if __name__ == '__main__': 5 # 已知 l 是可迭代對象,但不是 迭代器對象 6 ret = iter(l) 7 print(type(ret),issubclass(type(ret),Iterator))
可迭代對象 是不可以直接取值的,(之所以 for 循環可以取值,是for in 結構內部做了轉換 ,已經轉換為了 迭代器對象, )
Python 中如何 使用 迭代器對象:
通過next() 內置函數,或者 .__next__() 方法來取 迭代器對象中的值,
使用 迭代器對象 的特點:
優點:
1,節省內存
2,惰性機制,next()一次,取一個值
缺點:
1,速度慢
2,只能遍歷一次
Python 中 生成器 對象:
生成器對象 是迭代器對象 的一種,
構建 生成器對象 的方法:
1,生成器函數 的返回值,
什么是生成器函數呢? 函數中有 yield 關鍵字 即為生成器函數
1 def func(): 2 print('a') 3 print('a') 4 yield 3 5 6 print(func) # func 仍然是個 函數 <function func at 0x10E8EFA8> 7 ret = func() 8 print(ret) # ret 是個生成器對象 <generator object func at 0x0362F150>
2,生成器表達式 的返回值
1 ret = (i for i in range(10)) 2 print(ret) #<generator object <genexpr> at 0x0362F120>
1,生成器 函數:
在 Python 中,有 yield 的 函數 被稱為生成器函數 ,
生成器每次到 調用yield時 會暫停,而可以使用next()函數和send()函數恢復生成器。
1 def func(): 2 print('a') 3 a = yield 3 #暫停點 4 print("=======") 5 print(a) 6 print('b') 7 yield 2 8 9 ret = func() 10 # ret 為一個生成器對象 11 ret1 = next(ret) 12 print(ret1) 13 14 ret2 = next(ret) 15 print(ret2) 16 17 ''' 18 a 19 3 20 ======= 21 None 22 b 23 2 24 25 '''
1 def func(): 2 print('a') 3 a = yield 3 4 print("=======") 5 print(a) 6 print('b') 7 yield 2 8 9 ret = func() 10 # ret 為一個生成器對象 11 ret1 = next(ret) 12 print(ret1) 13 14 ret2 = ret.send({'name':'tom'}) 15 print(ret2) 16 ''' 17 a 18 3 19 ======= 20 {'name': 'tom'} 21 b 22 2 23 '''

1 def func(): 2 print('a') 3 a = yield 3 4 print("b") 5 return 1 6 yield 2 7 ret = func() 8 # ret 為生成器 9 ret1 = next(ret) # 第一次沒問題 10 ret2 = next(ret) # 第二次 return 就終止了生成器
yield from :

1 def func(): 2 yield [1,2,3,4] 3 4 ret = func() 5 # ret 為生成器 6 ret1 = next(ret) 7 print(ret1) 8 # =========================================== 9 def func(): 10 yield from [1,2,3,4] 11 ret2 = func() 12 # ret2 為生成器 13 ret3 = next(ret2) 14 print(ret3)

1 def func01(): 2 l1 = ['a','b','c'] 3 l2 = ['d','e','f'] 4 yield l1 5 yield l2 6 ret = func01() 7 # ret 為生成器 8 ret2 = next(ret) 9 print(ret2) 10 11 ret3 = next(ret) 12 print(ret3) 13 print('======') 14 15 #==================================================================================== 16 def func02(): 17 l1 = ['a','b','c'] 18 l2 = ['d','e','f'] 19 yield from l1 20 yield from l2 21 22 ret = func02() 23 # ret 為生成過期 24 ret2 = next(ret) 25 print(ret2) 26 27 ret3 = next(ret) 28 print(ret3) 29 30 ret4 = next(ret) 31 print(ret4) 32 33 ret5 = next(ret) 34 print(ret5) 35 36 ''' 37 ['a', 'b', 'c'] 38 ['d', 'e', 'f'] 39 ====== 40 a 41 b 42 c 43 d 44 '''
yield from 作用:
優化了內層循環, 下例:
1 def func01(): 2 for i in [1,2,3]: 3 for j in [4,5,6]: 4 yield j 5 ret = func01() 6 ###############二者相比 少了一層循環################## 7 def func02(): 8 for i in [1,2,3]: 9 yield [4,5,6] 10 ret = func02()
2,生成器 表達式 :
它只是把 列表 表達式 的[ ] 換成 ( ) 即可, 表達式的返回值 也是生成器對象 ,
但是,列表 表達式 費內存, 生成器表達式 則是很省內存,
Python 的 for in 結構:
Python 中使用迭代器對象,生成器對象不用 一步一步 next() ,
可以直接用 for in 結構 來遍歷,而且遍歷完 也不會 報錯,
Python 68個內置函數:
一帶而過:
all() any() bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int() iter() locals() next() oct() ord() pow() repr() round()
repr() 原型畢露 函數 :
1 # 正常打印 一個字符串 2 print("hello") # 看不到引號 3 4 print(repr("hello"))
all():
判斷一個可迭代對象 中 所有的元素都是真,才返回 True
any():
判斷一個可迭代對象 中 有一個元素都是真,即返回 True
重點:
abs() enumerate() filter() map() max() min() open() range() print() len() list() dict() str() float() reversed() set() sorted() sum() tuple() type() zip() dir()
classmethod() delattr() getattr() hasattr() issubclass() isinstance() object() property() setattr() staticmethod() super()
reversed():
它返回的是一個 迭代器對象, 它不會修改原來的數據
Python 中 匿名函數 :
所謂的匿名 指的是定義函數的時候 沒有函數名,但是調用的時候,還是得用函數名調用。
1 f = lambda x,y:x+y
ret = f(1,6) # 7
Python 中 閉包:
閉包: 在一個外函數中定義了一個內函數,內函數里 用了外函數作用域 的變量,並且 外函數的返回值是內函數的引用。
1 def outter(): 2 a = 10 3 def innter(): 4 print(a) 5 return innter 6 outter()()
一般情況下,如果一個函數結束,函數的內部所有東西都會釋放掉,還給內存,局部變量都會消失。
但是閉包是一種特殊情況,如果外函數在結束的時候發現有自己作用域的 變量將來會在內部函數中用到,就把這個 變量綁定給了內部函數,然后自己再結束。
這個變量會一直存在在內存中,啥時候 調用內函數,啥時候用它 。
1 def outter(): 2 l = [1,2,3,4,5] 3 def innter(): 4 l.append(6) # 可變類型 l 是可以不用 nonlocal 的 5 print(l) 6 return l 7 return innter 8 ret = outter() 9 # 此時outter() 已經結束了 10 for i in ret(): 11 print(i)
Python 中 裝飾器 :
當多個裝飾器的時候,執行是從下往上執行,
裝飾器 本質就是一個函數,這個函數的參數是 要被裝飾的函數名字,
python裝飾器(fuctional decorators)就是用於拓展原來函數功能的一種函數,目的是在不改變原函數名(或類名)的情況下,給函數增加新的功能。
這個函數的特殊之處在於它的返回值也是一個函數,這個函數是內嵌“原“”函數。
1, 被裝飾的函數 無參數 無返回值 :
a, 內函數 無返回值:
1 import time 2 def func(): 3 print("hello world") 4 time.sleep(1) # 模擬代碼執行 5 6 # 為了能不改func() 代碼的前提下 測量func執行時間,要使用裝飾器了 7 8 def dec(f): # 裝飾器函數的參數f 是個要被裝飾的 函數 9 def inner(): 10 t1 = time.time() 11 f() 12 t = time.time() - t1 13 print("耗時:",t) 14 return inner 15 # 此時 dec 就是個裝飾器函數 16 dec(func)()
b, 內函數 有返回值:
1 import time 2 def func(): 3 print("hello world") 4 time.sleep(1) # 模擬代碼執行 5 print("hello world") 6 7 # 為了能不改func() 代碼的前提下 測量func執行時間,要使用裝飾器了 8 9 def dec(f): # 裝飾器函數的參數 f 是個要被裝飾的 函數 10 def inner(): 11 t1 = time.time() 12 f() 13 t = time.time() - t1 14 return t 15 return inner 16 # 此時 dec 就是個裝飾器函數 17 ret = dec(func)() 18 print("總耗時: ",ret)
2, 被裝飾的函數 有參數 有返回值 :
內函數也有返回值:
1 import time 2 def func(name,age): 3 print("hello I am ",name) 4 time.sleep(1) # 模擬代碼執行 5 return age 6 7 def dec(f): # 裝飾器函數的參數f 是個要被裝飾的 函數 8 def inner(name,age): 9 t1 = time.time() 10 ret = f(name,age) 11 t = time.time() - t1 12 print("func 函數的返回值: ",age) 13 return t 14 return inner 15 16 ret = dec(func)("tom",18) 17 print("總耗時: ",ret)
但是,上面的調用方式太麻煩,Python 提供了一個語法糖 即@ + 裝飾器名字 ,
語法糖(Syntactic sugar)是英國計算機科學家彼得·約翰·蘭達發明的一個術語,指計算機語言中添加的某種語法,這些語法沒有給程序增加新功能,但是對於程序員更“甜蜜”。語法糖提供了更易讀的編碼方式,可以提高開發效率。
1 import time 2 3 def dec(f): # 裝飾器函數的參數f 是個要被裝飾的 函數 4 def inner(name,age): 5 t1 = time.time() 6 ret = f(name,age) 7 t = time.time() - t1 8 print("func 函數的返回值: ",age) 9 return t 10 return inner 11 12 @dec # --> func = dec(func) 13 def func(name,age): 14 print("hello I am ",name) 15 time.sleep(1) # 模擬代碼執行 16 return age 17 18 ret = func("tom",18) 19 print("總耗時: ",ret)
@ 后接的是個函數,不是直接的函數名,

1 import time 2 def test(): 3 def dec(f): # 裝飾器函數的參數f 是個要被裝飾的 函數 4 def inner(name,age): 5 t1 = time.time() 6 ret = f(name,age) 7 t = time.time() - t1 8 print("func 函數的返回值: ",age) 9 return t 10 return inner 11 return dec 12 13 @test() # 這里是先執行 test() 函數,這個函數返回dec 裝飾器 14 def func(name,age): 15 print("hello I am ",name) 16 time.sleep(1) # 模擬代碼執行 17 return age 18 19 ret = func("tom",18) 20 print("總耗時: ",ret)
多個裝飾器 裝飾同一個函數:
1 import time 2 3 def dec(f): # 裝飾器函數的參數f 是個要被裝飾的 函數 4 def inner(name,age): 5 t1 = time.time() 6 ret = f(name,age) 7 t = time.time() - t1 8 print("func 函數的返回值: ",age) 9 return t 10 return inner 11 12 def dec2(f): 13 def inner(name,age): 14 f(name,age) 15 print("你好,",name,"我再次裝飾一下") 16 return inner 17 @dec 18 @dec2 # --> func = dec(dec2(func)) 19 def func(name,age): 20 print("hello I am ",name) 21 time.sleep(1) # 模擬代碼執行 22 return age 23 24 ret = func('tom',18) # --> dec(dec2(func))('tom',18) 25 print("總耗時: ",ret)
多個裝飾器的 順序:與聲明順序 相反,
Python 中 內置裝飾器 :
在Python中有三個內置的裝飾器,都是跟class相關的:staticmethod、classmethod 和property。
- staticmethod 是類靜態方法,其跟成員方法的區別是沒有 self 參數,並且可以在類不進行實例化的情況下調用
- classmethod 與成員方法的區別在於所接收的第一個參數不是 self (類實例的指針),而是cls(當前類的具體類型)
- property 是屬性的意思,表示可以通過通過類實例直接訪問的信息
property 裝飾器 的 作用是:把一個方法 偽裝成一個屬性。 這個方法必須要有返回值, 而且一般它只有self 一個參數。
property裝飾器的應用場景 ---- 結合私有屬性使用 :

1 class Person: 2 def __init__(self,name): 3 self.__name = name 4 5 @property 6 def name(self): 7 return self.__name 8 9 p = Person('jack') 10 print(p.name) 11 p.name = 'tom' 12 ''' 13 jack 14 Traceback (most recent call last): 15 File "D:/.project/project_python/test02/bin/start.py", line 11, in <module> 16 p.name = 'tom' 17 AttributeError: can't set attribute 18 '''
property 進階:
上面代碼,如果我們是真的想改個名字,怎么辦呢?

1 class Person: 2 def __init__(self,name): 3 self.__name = name 4 5 @property 6 def name(self): 7 return self.__name 8 9 @name.setter 10 def name(self,new_val): 11 self.__name = new_val 12 13 14 p = Person('jack') 15 print(p.name) # 此時調用的是 @property裝飾的 name 16 p.name = 'tom' # 此時調用的是 @name.setter裝飾的 name 17 print(p.name) # 此時調用的是 @property裝飾的 name 18 ''' 19 jack 20 tom 21 '''
參看: https://www.liaoxuefeng.com/wiki/897692888725344/923030547069856
Python 中 模塊 :
什么是模塊:本質的就是 .py 文件, 它是封裝語句的 最小單位, 一行語句也得放到一個模塊中,
sys 模塊:
模塊對象的使用:

1 import sys 2 def f(): 3 print('aaaaaaa') 4 num = 10 5 6 m_obj = sys.modules['__main__'] 7 print(m_obj) 8 m_obj.f() 9 print(m_obj.num)
datetime模塊:

1 from datetime import date,time,timedelta 2 # 年月日 3 date = date(2020,2,27) 4 print(date) 5 6 # 時分秒 7 time = time(10,10,10) 8 print(time) 9 10 # 時間 delta 11 # timedelta()

1 import datetime 2 3 4 t0 = datetime.datetime(2010,2,20) 5 t1 = datetime.datetime(2020,3,30) 6 7 res = t1 - t0 8 print(res) 9 print(type(res)) 10 print(res.days)
json 和 pickle 模塊:
json 模塊,不能完全將Python中 的全部數據類型 都序列化,
於是,就有了 pickle 模塊,它可以完全序列化, 使用方法 和 json 模塊一樣, dump 和 load
二者區別: json 是跨語言的, pickle 不跨語言(只是Python 中使用),
hashlib 模塊:
它 封裝了一些 用於加密的類,
加密的目的:是用於 判斷 和 驗證, 不是用於解密,
不同模塊之間導入方法:
直接將項目 目錄添加到 sys.path ,然后每次 使用from import 使用 例如:
1 import sys 2 sys.path.append("../..") # 添加了 項目的 根目錄 3 from core import src 4 from lib import test 5 def run(): 6 print("開始執行吧") 7 src.main() 8 9 if __name__ == '__main__': 10 run()
re模塊:
正則表達式
https://www.geeksforgeeks.org/regular-expression-python-examples-set-1/
https://www.geeksforgeeks.org/regular-expressions-python-set-1-search-match-find/
logging模塊:
為什么要有log ,
1,排錯
2,做數據分析
3,追責
可以通過 logging.basicConfig(level = logging.DEBUG) 類似的調整等級,
寫日志,都是自己覺得 可能出錯的地方,寫一下,(都是自己寫)
日志的格式 設置 -- logging.basicConfig() 來設置:
1 import logging 2 3 logging.basicConfig( 4 format='%(asctime)s | %(name)s | %(levelname)s |%(module)s: %(lineno)d | %(message)s', 5 datefmt='%Y-%m-%d %H:%M:%S %p' 6 ) 7 logging.warning("func(1+20)") '''2020-02-27 17:27:25 PM | root | WARNING |start: 7 | func(1+20) '''
上面都是輸出到 屏幕,下面輸出到 文件:
還是在 logging.basicConfig()
使用關鍵字 filename 來指定,一般文件命名以.log 結尾,而且,是追加的方式 寫文件,

1 import logging 2 3 logging.basicConfig( 4 format='%(asctime)s | %(name)s | %(levelname)s |%(module)s: %(lineno)d | %(message)s', 5 datefmt='%Y-%m-%d %H:%M:%S %p', 6 filename='my.log' 7 ) 8 logging.warning("func(1+20) ")
如何同時 輸出到 屏幕 和 文件?
這時要用handlers 來指定 要輸出的文件描述符了, 此時也可以指定寫入文件時候的編碼了,
1 import logging 2 3 4 fh = logging.FileHandler('my.log',encoding="utf8") 5 sh = logging.StreamHandler() 6 logging.basicConfig( 7 format='%(asctime)s | %(name)s | %(levelname)s |%(module)s: %(lineno)d | %(message)s', 8 datefmt='%Y-%m-%d %H:%M:%S %p', 9 handlers=[fh,sh] 10 ) 11 logging.warning("func(1+20) ")
日志切割:
1 import time 2 import logging 3 from logging import handlers 4 5 sh = logging.StreamHandler() 6 rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024,backupCount=5) # 按大小切割 最多5個備份 7 fh = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=5, encoding='utf-8') # 按時間切, when:按秒 ,
8 logging.basicConfig( 9 format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', 10 datefmt='%Y-%m-%d %H:%M:%S %p', 11 handlers=[fh,sh,rh], 12 level=logging.ERROR 13 ) 14 15 for i in range(1,100000): 16 time.sleep(1) 17 logging.error('KeyboardInterrupt error %s'%str(i))
Python 面向對象相關知識:
參看:
https://www.cnblogs.com/linhaifeng/articles/6204014.html#_label6
https://www.cnblogs.com/linhaifeng/articles/8029564.html
類對象 和 實例對象 的 屬性字典:
1 class Demo: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def run(self): 7 print("跑") 8 9 # 類對象的 屬性字典 10 print(Demo.__dict__) 11 demo = Demo('tom',18) 12 # 實例對象的 屬性字典 13 print(demo.__dict__) 14 ''' 15 {'__module__': '__main__', '__init__': <function Demo.__init__ at 0x10FCE0C0>, 'run': <function Demo.run at 0x10FCE078>, '__dict__': <attribute '__dict__' of 'Demo' objects>, '__weakref__': <attribute '__weakref__' of 'Demo' objects>, '__doc__': None} 16 {'name': 'tom', 'age': 18} 17 '''
類中的 一般方法 和 類方法 和 靜態方法:
1 class Demo: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def run(self): 7 print("跑") 8 9 @classmethod 10 def test(cls): 11 print("hahaha") 12 13 @staticmethod 14 def test02(): 15 print("我去") 16 17 18 Demo.test() # 類方法 (其實里面有 參數 ) 19 Demo.test02() # 靜態方法 (沒參數 ) 20 #=========== 21 demo = Demo("name",18) 22 demo.run() # 一般方法 (里面有 參數)
注:類方法 和 靜態方法 在使用的時候,其實是差不多的,
類的 類 type(元類) :
類的類 是type ,即類對象 是由 type 類產生的,
type 是Pyhton一個內置類,Python中,任何class 定義的類 其實都是type 實例化的對象,
其實定義類有兩種方式:
1,用class
2, 用type 類
1 Demo = type('Demo',(object,),{'name':'tom',"age":18}) 2 print(Demo.__dict__) 3 4 class Demo02: 5 name:'tom' 6 age:18 7 print(Demo02.__dict__ ) 8 ''' 9 {'name': 'tom', 'age': 18, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Demo' objects>, '__weakref__': <attribute '__weakref__' of 'Demo' objects>, '__doc__': None} 10 {'__module__': '__main__', '__annotations__': {'name': 'tom', 'age': 18}, '__dict__': <attribute '__dict__' of 'Demo02' objects>, '__weakref__': <attribute '__weakref__' of 'Demo02' objects>, '__doc__': None} 11 '''

1 def init(self,name,age): 2 self.name = name 3 self.age = age 4 def run(self): 5 print("跑") 6 7 Demo = type('Demo',(object,),{'__init__':init,'run':run}) 8 demo =Demo('tom',18) 9 print(demo.__dict__) 10 demo.run() 11 12 # ========================================================== 13 14 class Demo02: 15 def __init__(self,name,age): 16 self.name = name 17 self.age = age 18 def run(self): 19 print("跑") 20 demo02 =Demo02('tom',18) 21 print(demo02.__dict__) 22 demo02.run()
type() 的三個參數:1,類名,2,繼承的對象(必須是 元組),3類對象的 屬性字典
metaclass 關鍵字 :
Python 內置的元類 只有一個type ,
其實,平時 用class 定義類的時候,默認就加了 metaclass = type 的, 只是我們不知道,
1 class Demo: 2 pass 3 4 class Demo(metaclass=type): 5 pass
自定義元類 :
因為type 是Python 內置的元類,我們可以通過自己創建繼承 type 就構建成了 我們自己的元類,
1 class MyType(type,metaclass=type): # metaclass=type 可以省略 2 pass
通過自定義元類 研究 “實例化”:
實例化就是 類名 + 括號,
1 class MyType(type,metaclass=type): # metaclass=type 可以省略 2 def __init__(self,a,b,c): 3 print('自定義 元類 的 init 方法 ') 4 print(a) 5 print(b) 6 print(c) 7 def __call__(self,a): 8 print("hhh ") 9 print(a) 10 obj = object.__new__(self) # 這里的self 是 Demo 類對象,生成的obj 是demo 對象 11 self.__init__(obj,a) 12 return obj 13 14 15 class Demo(metaclass=MyType): # 如果不寫 metaclass ,默認是 metaclass = type 16 name = 'tom' 17 def __init__(self,age): 18 self.age = age 19 20 demo = Demo(18) 21 # Demo(18) 會做如下 操作: 22 #第一步:MyType('Demo',(),{{'__module__': '__main__', '__qualname__': 'Demo', 'name': 'tom', '__init__': <function Demo.__init__ at 0x10E4E0C0>}}) 23 #第二步:把 18 參數 傳給MyType 中的 __call__ 方法 24 #第三步:在__call__ 方法中,創建demo 對象,初始化 ,並返回
總結:所以平時 我們實例化的過程實際上是 type 幫我們創建demo 對象,然后 初始化它,然后返回給我們的, 上面用重寫的方式,重現了這一過程,
類對象 屬性 和 實例對象屬性的 尋找范圍:
1 class MyType(type,metaclass=type): # metaclass=type 可以省略 2 name = "aaa" 3 def __init__(self,*args,**kwargs): 4 self.name = 'aaa2' 5 pass 6 7 def __call__(self, *args, **kwargs): 8 obj = object.__new__(self) 9 self.__init__(obj,*args,**kwargs) 10 return obj 11 12 class Demo(object,metaclass=MyType): # 其實 默認也是繼承 object 13 name = 'bbb' 14 def __init__(self): 15 self.name = 'bbb2' 16 pass 17 18 class Demo2(Demo): # metaclass=MyType 也繼承過來了 19 name = 'ccc' 20 def __init__(self): 21 self.name = 'ccc2' 22 pass 23 24 class Demo3(Demo2): # metaclass=MyType 也繼承過來了 25 name = 'ddd' 26 def __init__(self): 27 self.name = 'ddd2' 28 pass 29 30 # print(Demo3.name) # 先找 aaa2-->ddd --> ccc ---> bbb --> aaa 類對象的屬性 尋找范圍 31 32 demo = Demo3() 33 print(demo.name) # 先找 ddd2 --> aaa2 -->ddd --> ccc --> bbb 對象的屬性 尋找范圍
類 中的 __new__() 函數 :
1 class MyType(type,metaclass=type): # metaclass=type 可以省略 2 def __init__(self,name,bases,attrs): 3 print('4') 4 print(name) 5 print(bases) 6 print(attrs) 7 8 def __new__(cls, name,bases,attrs): #新建 MyType 它自己,之后會初始化 它 9 print('2') 10 print(name) 11 print(bases) 12 print(attrs) 13 # 重寫 type 中 __new__ 14 return type.__new__(cls,name,bases,attrs) 15 16 class Demo(object,metaclass=MyType): # 其實 默認也是繼承 object 17 print('1') 18 def __init__(self): 19 print('3')
'''
1
2
Demo
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'Demo', '__init__': <function Demo.__init__ at 0x1091E0C0>}
4
Demo
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'Demo', '__init__': <function Demo.__init__ at 0x1091E0C0>}
'''
Python 中的組合 :
一個類的對象 作為了另一個類的屬性,
1 class Course: 2 def __init__(self,course_name,dur,price): 3 self.course_name = course_name 4 self.dur = dur 5 self.price =price 6 7 class Banji: 8 def __init__(self,name,course): 9 self.name = name 10 self.course = course 11 12 bj1 = Banji('八年級',Course('數學','10個月',10000)) 13 bj2 = Banji('八年級',Course('英語','5個月',1000))
Python 中的 抽象類 :
與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化
1 from abc import ABCMeta,abstractmethod 2 class Animal(metaclass=ABCMeta): 3 ''' 4 所有動物 都會跑 和 吃 5 ''' 6 @abstractmethod 7 def run(self): 8 pass 9 10 @abstractmethod 11 def eat(self): 12 pass 13 14 class Dog(Animal): 15 def __init__(self,name): 16 self.name = name 17 18 dog = Dog('tom')
Python 中的 多態:
處處是 多態,因為 Python 是 不是很 重視類型 的語言,
鴨子類型:
https://www.jianshu.com/p/e97044a8169a
Python 中的 super() :
super() 尋找是 按照 mro 順序 尋找父類的 ,
Python 中的反射:
反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力。這一概念的提出很快引發了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所采用,並在Lisp和面向對象方面取得了成績。
上面一點用都沒有,就四個內置函數 ,用於判斷一個對象有沒有 一個屬性的
hasattr()
getattr()
setattr()
delattr()

1 class Person: 2 def __init__(self,name): 3 self.name = name 4 def run(self): 5 print('run') 6 7 8 p = Person('jack') 9 ret = hasattr(p,'name') 10 print(ret) 11 12 #================================ 13 print(p.name) 14 setattr(p,'name','tom') 15 print(p.name) 16 17 #================================ 18 ret = getattr(p,'name') 19 print(ret) 20 21 #================================ 22 print(p.__dict__) 23 delattr(p,'name') 24 print(p.__dict__) 25 26 #===============類也是 對象================= 27 ret = hasattr(Person,'run') 28 print(ret) 29 30 #===============模塊也是 對象================= 31 import sys 32 m = sys.modules[__name__] 33 ret = hasattr(m,'Person') 34 print(ret) 35 36 ret = getattr(m,'Person') 37 print(ret)
Python 中的 類的 三個保留方法 __setattr__ ,__getattr__,__delattr__:
1,__setattr__: 給對象 添加/修改 屬性的時候會 調用它
2,__getattr__: 在使用對象 調用一個不存在的屬性的時候 會調用它 (p.name = 'tom' 這不算調用屬性了,這是設置屬性 )
3,__delattr__: 在刪除 對象的 屬性的時候回調用它
如果我們不寫 __getattr__() 方法的話,如果直接引用一個不存在的 的屬性,那么此時就會報錯,如果寫了就不報錯了,這也是它的作用。

1 class Person: 2 def __init__(self,name): 3 self.name = name 4 5 def run(self): 6 print('run') 7 8 def __setattr__(self, key, value): 9 self.__dict__[key] = value 10 11 12 # def __getattr__(self, item): 13 # print(item) 14 # print('h') 15 16 def __delattr__(self, item): 17 print(item) 18 print('w') 19 20 p = Person('jack') 21 p.age = 19 22 23 # p.sex

1 ''' 2 class Person: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 p = Person('jack') 8 p.name = 'tom' 9 p.age = 18 10 ''' 11 #需求 控制 name 必須是字符串, 控制 age 的范圍是 [30-50] 12 class Person: 13 def __init__(self,name,age): 14 self.name = name 15 self.age = age 16 17 def __setattr__(self, key, value): 18 if key == 'name': 19 if type(value) == str: 20 self.__dict__[key] = value 21 else: 22 print('name 屬性類型應為字符串') # 此時就不會 設置到self 的屬性字典中 23 if key == 'age': 24 if type(value) == int: 25 if value > 50 or value < 30: 26 print('age 屬性的范圍有誤 ,應為 [30,50] ') 27 else: 28 self.__dict__[key] = value 29 else: 30 print('age 屬性類型應為int') 31 32 33 # p = Person(10,18) # name 屬性類型應為字符串 age 屬性的范圍有誤 ,應為 [30,50] 34 # p = Person('tom',18) #age 屬性的范圍有誤 ,應為 [30,50] 35 p = Person('tom',30) # √ 36 p = Person('tom',51) # age 屬性的范圍有誤 ,應為 [30,50]
Python 中的 包裝 :
基於標准數據類型來定制我們自己的數據類型。
1 class List_Str(list): 2 def append(self,val): 3 if type(val) == str: 4 super().append(val) 5 else: 6 print("添加失敗,列表中只允許添加字符串") 7 8 9 l = List_Str() 10 l.append(10) 11 print(l) 12 13 #================================ 14 l.append('tom') 15 l.append('jack') 16 print(l) 17 ''' 18 添加失敗,列表中只允許添加字符串 19 [] 20 ['tom', 'jack'] 21 '''
這就是包裝,說到底 還是封裝,
Python 中的 授權:
1 class Open: 2 def __init__(self,filename,mode='r',encoding='utf8'): 3 self.f = open(filename,mode,encoding=encoding) 4 self.name = 'tom' 5 6 def write(self,line): 7 # 控制用戶寫的內容 8 if 'hello' in line: 9 print('您的內容 違法,清重新輸入 hhh') 10 else: 11 self.f.write(line) 12 13 def __getattr__(self, item): 14 return getattr(self.f,item) 15 16 ''' 17 op = Open('test.txt','w') 18 # op.write('hello world') # 違法 19 op.write('hell world') 20 ''' 21 22 #================================ 23 # 下面再 讀文件 24 op = Open('test.txt') 25 ret = op.read() # 此時會調用 __getattr__, 然后,就可以獲取到 文件對象的 read() 方法了 26 print(ret)
說到底,還是 封裝,
Python 類 中的 __getattribute__ 保留方法 :
1,如果對象 調用一個已存在的 屬性,它是不會調用 __getattr__ 的, 會調用 __getattribute__方法,
2,如果對象調用一個不存在的 屬性,
a) 如果此時只有 __getattr__ 沒有 __getattribute__,那么 調用 __getattr__ ,
b) 如果此時一旦存在 __getattribute__ 沒有 ,那么無論存不存在__getattr__ ,都只是 調用 __getattribute__ ,
一句話:__getattribute__ 是大哥, __getattrr__ 是小弟,
其實, 真正觸發 __getattr__ 的原因是 一個異常的拋出, AttributeError ,所以,可以在大哥中 拋出這個異常,就可以召喚 小弟了。

1 class Demo: 2 def __init__(self,name ): 3 self.name = name 4 5 def __getattr__(self, item): 6 print(item) 7 print('h') 8 9 def __getattribute__(self, item): 10 print(item ) 11 print('hh') 12 raise AttributeError("小弟快來...") 13 14 demo =Demo('tom') 15 demo.name 16 demo.age
總結:
__getattribute__ :是只要是 調用了 屬性,不管有沒有,就觸發它,
__getattr__ : 捕獲 AttributeError 異常時,才會觸發 。
python 類中的 保留方法 __getitem,__setitem__,__delitem__:
它們 和 attr 很類似,主要是 attr 是點操作, 它們是 [ ] 操作,
__getitem__:
1 class Demo: 2 def __init__(self,name ): 3 self.name = name 4 5 def __getitem__(self, item): 6 print(item) 7 8 9 demo =Demo('tom') 10 # demo.name #此時不觸發 __getitem__ 11 demo['name'] #這種形式的時候觸發 12 demo['age'] # 沒有該屬性也觸發

1 class Demo: 2 def __init__(self,name ): 3 self.name = name 4 5 def __getitem__(self, item): 6 print(item) 7 8 def __setitem__(self, key, value): 9 print(key) 10 print(value) 11 12 def __delitem__(self, key): 13 print(key) 14 15 demo =Demo('tom') 16 demo['name'] = 'jack' 17 18 del demo['name']
python 中的 另一個保留方法---- 析構方法 __del__ :
析構方法,當對象在內存中被釋放時,自動觸發執行。
python 類 中的 保留方法 __get__ , __set__ , __delete__ :
它們的調用時機不是 實例對象操作屬性的時候,

1 class Demo: 2 def __init__(self,name ): 3 self.name = name 4 5 def __get__(self, instance, owner): 6 print(instance) 7 print(owner) 8 9 def __set__(self, instance, value): 10 print(instance) 11 print(value) 12 13 def __delete__(self, instance): 14 print(instance) 15 16 17 demo =Demo('tom') 18 demo.name = 'jack' 19 del demo.name
它們的調用時機,是這個類的對象 作為另一個類的類屬性的時候,
1 class MyStr: 2 def __get__(self, instance, owner): 3 print('get') 4 print(instance) # 這里的instance 指的是下面的 People 對象 5 print(owner) 6 def __set__(self, instance, value): 7 print('set') 8 print(instance) 9 print(value) 10 def __delete__(self, instance): 11 print('delete') 12 print(instance) 13 14 class People: 15 name = MyStr() 16 def __init__(self,name,age): 17 self.name = name #對象字典中 不會有name 了,跑到 類屬性字典了, 18 self.age = age 19 20 p = People('tom',18) 21 print(p.__dict__) 22 print(People.__dict__) 23 print("#================================") 24 p.name 25 print("#================================") 26 del p.name
它們的應用場景:

1 class MyType: 2 def __init__(self,key,type): 3 self.type = type 4 self.key = key 5 6 def __set__(self, instance, value): 7 if type(value) == self.type: 8 instance.__dict__[self.key] = value 9 else: 10 print(self.key,"的類型應該是 ",self.type ) 11 12 class People: 13 name = MyType('name',str) 14 age = MyType('age',int) 15 def __init__(self,name,age): 16 self.name = name #對象字典中 不會有name 了,跑到 類屬性字典了, 17 self.age = age #對象字典中 不會有age ,跑到 類屬性字典了, 18 19 p = People('tom',18) 20 print('===============') 21 p1 = People('tom','18') 22 print('===============') 23 p2 = People(18,18) 24 print('===============') 25 p3 = People(18,'18') 26 print('===============')
模仿內置裝飾器 @property :
一個類 也可以成為一個裝飾器,(不僅僅是 函數)

1 class MyProperty: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): 6 print(instance) # Demo 實例對象 7 print(owner) # instance 的類型 8 return self.func(instance) 9 10 11 class Demo: 12 def __init__(self,w,h): 13 self.w = w 14 self.h = h 15 16 @MyProperty # --> area = MyProperty(area) # 此時,area 是個類變量~ 會觸發 __get__方法 17 def area(self): 18 return self.w*self.h 19 20 demo = Demo(18,10) 21 print(demo.area)
再看 @property :

1 ''' 2 3 class Demo: 4 def __init__(self,w,h): 5 self.w = w 6 self.h = h 7 8 @property 9 def area(self): 10 return self.w*self.h 11 12 demo = Demo(18,10) 13 14 # demo.area = 18 15 # del demo.area 16 # 此時如果是真的想給 area 賦值 ,或者 刪除 area 均會報錯, 17 ''' 18 #============================================================ 19 class Demo: 20 def __init__(self,w,h): 21 self.w = w 22 self.h = h 23 self.__area = w*h 24 25 @property 26 def area(self): 27 if not hasattr(self,"_Demo__area"): 28 print('沒有此屬性') 29 return None; 30 return self.__area 31 32 @area.setter 33 def area(self,val): 34 self.__area = val 35 36 @area.deleter 37 def area(self): 38 del self.__area 39 40 demo = Demo(18,10) 41 print(demo.area) 42 #================================ 43 demo.area = 18 44 print(demo.area) 45 # 46 #================================ 47 del demo.area 48 print('============================') 49 print(demo.area) 50 51 print('============================') 52 demo.area = 10 53 print(demo.area)
python 類 中的 保留方法 __call__ :
如果 實現了它,那么就可以用對象 + 括號 了,
1 class Demo: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def __call__(self, *args, **kwargs): 7 print('ha') 8 9 Demo('tom',18)() 10 ''' 11 ha 12 '''
Python 中 類的裝飾器:
裝飾器 不僅可以裝飾函數 也可以裝飾類,
1 class Demo: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def dec(c): 7 def inner(name,age): 8 print("給類 加額外功能") 9 obj = c(name,age) 10 return obj 11 return inner 12 13 ret = dec(Demo)('tom',18) 14 print(ret) 15 print(ret.name) 16 print(ret.age)
1 def dec(c): 2 def inner(name,age): 3 print("給類 加額外功能") 4 obj = c(name,age) 5 return obj 6 return inner 7 8 @dec 9 class Demo: 10 def __init__(self,name,age): 11 self.name = name 12 self.age = age 13 14 demo = Demo('tom',18) 15 print(demo.name) 16 print(demo.age)
單例模式:
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。
這種模式涉及到一個單一的類,該類確保只有單個對象被創建。
注意:
- 1、單例類只能有一個實例。
- 2、單例類必須自己創建自己的唯一實例。
- 3、單例類必須給所有其他對象提供這一實例。
單例模式的應用場景:
1. Windows的Task Manager(任務管理器)就是很典型的單例模式(這個很熟悉吧),想想看,是不是呢,你能打開兩個windows task manager嗎? 不信你自己試試看哦~
2. windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統運行過程中,回收站一直維護着僅有的一個實例。
3. 操作系統的文件系統,也是大的單例模式實現的具體例子,一個操作系統只能有一個文件系統。
Python實現單例模式:
參看:
https://www.cnblogs.com/huchong/p/8244279.html
最簡單的單例模式是 :Python的一個模塊 即可以實現單例模式,

1 ''' 2 class Demo: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 demo = Demo('tom',18) 8 print(demo) 9 demo2 = Demo('a',20) 10 print(demo2) 11 12 <__main__.Demo object at 0x02FBF990> 13 <__main__.Demo object at 0x02FBFA70> 14 15 ''' 16 # 如何將 上面 改為一個單例模式的 類呢? 17 class Demo: 18 __singleInstance = None 19 20 def __init__(self,name,age): 21 self.name = name 22 self.age = age 23 24 def __new__(cls, *args, **kwargs): 25 if cls.__singleInstance is None: 26 cls.__singleInstance = object.__new__(cls) 27 return cls.__singleInstance 28 29 30 demo = Demo('tom',18) 31 print(demo) 32 demo2 = Demo('a',20) 33 print(demo2) 34 ''' 35 <__main__.Demo object at 0x02BCFA30> 36 <__main__.Demo object at 0x02BCFA30> 37 '''
其實,默認 調用new 也是object.__new__( ) 。
new的調用時機,它在init 之前,其實它是在call里被調用的,
Python 的with 關鍵字 的使用 :
1 class Demo: 2 def __init__(self,name): 3 self.name = name 4 5 def __enter__(self): 6 print("我來了 ") 7 return 'zcb' 8 9 def __exit__(self, exc_type, exc_val, exc_tb): 10 # __exit__()方法的3個參數,分別代表異常的類型、值、以及堆棧信息 11 print('===========') 12 print(exc_type) 13 print(exc_val) 14 print(exc_tb) 15 print("我走了") 16 17 # with 后跟的對象的類中 一定要有 __enter__ 和 __exit__方法,不然報錯 18 # as 可以省略, as 后跟的是 __enter__ 的返回值 19 with Demo('tom') as ret: 20 print(ret)
我來了
zcb
===========
None
None
None
我走了

1 class Demo: 2 def __enter__(self): 3 print('1') 4 return self 5 6 def run(self): 7 print('跑') 8 print(1/0) 9 10 def __exit__(self, exc_type, exc_val, exc_tb): 11 print(exc_type) 12 print(exc_val) 13 print(exc_tb) 14 15 with Demo() as d: # d是__enter__ 的返回值 16 d.run() 17 18 ''' 19 Traceback (most recent call last): 20 File "D:/.project/project_python/test02/bin/start.py", line 16, in <module> 21 d.run() 22 File "D:/.project/project_python/test02/bin/start.py", line 8, in run 23 print(1/0) 24 ZeroDivisionError: division by zero 25 1 26 跑 27 <class 'ZeroDivisionError'> 28 division by zero 29 <traceback object at 0x102CC350> 30 '''
網絡通信:
粘包 現象:
TCP才可能出現 粘包現象,UDP 不可能粘包,
所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。
此外,發送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化算法把這些數據合成一個TCP段后一次發送出去,這樣接收方就收到了粘包數據。
參看:
https://www.cnblogs.com/steve214/p/10022692.html
解決粘包的方法:設置邊界 ,
發送消息之前,先發送消息的字節長度,然后,再發送真實的消息。注:消息的發送長度的 字節要固定,因為要防止 長度 和 真實消息 也粘在一起。

1 import socket 2 ''' 3 server 端 4 ''' 5 sk = socket.socket() 6 7 sk.bind(('127.0.0.1',8080)) 8 sk.listen() 9 10 import time 11 12 while 1: 13 conn,addr = sk.accept() 14 while 1: 15 time.sleep(30) 16 size = conn.recv(4) #先接受 消息 字節數 17 print(size) 18 size = int(size.decode().zfill(4)) 19 data = conn.recv(size) 20 print(data) 21 22 d = input(">>> [server]") 23 conn.send(str(len(d.encode())).zfill(4).encode()) # 先發送 消息字節數 24 conn.send(d.encode()) 25 26 27 conn.close() 28 29 sk.close()

1 import socket 2 ''' 3 client 端 4 ''' 5 sk = socket.socket() 6 7 sk.connect(('127.0.0.1',8080)) 8 9 while 1: 10 d = input(">>> [client]") 11 sk.send(str(len(d.encode())).zfill(4).encode()) # 先發送 消息字節數 12 sk.send(d.encode()) 13 14 size = sk.recv(4) # 先接受 消息 字節數 15 print(size) 16 size = int(size.decode().zfill(4)) 17 data = sk.recv(size) 18 print(data) 19 20 sk.close()
但是,上面代碼里 最多接受字節的長度是 9999,也就是最多10000 個字節(有點少), 如果多於,就不行了, 上面用 zfill ( ) 直接在 左面加0 , 不是很好,
Python有個模塊,struct ,
參看: https://www.cnblogs.com/leomei91/p/7602603.html
它的pack() 可以將 Python中的數據 按照 fmt ,轉為 對應的字節流對象。
它的unpack() 可以將 字節流對象,轉換回 python 中的數據 。

1 import struct 2 3 a = 111111111 4 b = 111111 5 c = 1 6 7 ret1 = struct.pack('i',a) 8 ret2 = struct.pack('i',b) 9 ret3 = struct.pack('i',c) 10 11 print(ret1) 12 print(ret2) 13 print(ret3) 14 15 print(struct.unpack('i',ret1)) 16 print(struct.unpack('i',ret2)) 17 print(struct.unpack('i',ret3))
所以,我們可以將我們的 len 用 struct.pack() 轉換為 二進制形式(int ,4個字節), 這4個字節 最大的數 是 2^32 - 1 (4294967295) ,
所以,這已經足夠我們使用了, 4294967295個字節是 4294967295/(1024 ^2 ) = 3.9G ,所以,單次傳輸 不可能這么大的數據量。

1 import socket 2 import struct 3 ''' 4 server 端 5 ''' 6 sk = socket.socket() 7 8 sk.bind(('127.0.0.1',8080)) 9 sk.listen() 10 11 import time 12 13 while 1: 14 conn,addr = sk.accept() 15 while 1: 16 time.sleep(10) 17 size = conn.recv(4) #先接受 消息 字節數 18 size = struct.unpack('i',size)[0] 19 print(size) 20 data = conn.recv(size) 21 print(data) 22 23 d = input(">>> [server]") 24 conn.send(struct.pack('i',len(d.encode()))) # 先發送 消息字節數 25 conn.send(d.encode()) 26 27 28 conn.close() 29 sk.close()

1 import socket 2 import struct 3 ''' 4 client 端 5 ''' 6 7 sk = socket.socket() 8 9 sk.connect(('127.0.0.1',8080)) 10 11 while 1: 12 d = input(">>> [client]") 13 14 sk.send(struct.pack('i',len(d.encode()))) # 先發送 消息字節數 15 sk.send(d.encode()) 16 17 size = sk.recv(4) # 先接受 消息 字節數 18 size = struct.unpack('i',size)[0] 19 print(size) 20 data = sk.recv(size) 21 print(data) 22 23 sk.close()
大文件 多次傳輸 :

1 import socket 2 import struct 3 ''' 4 server 端 5 ''' 6 sk = socket.socket() 7 8 sk.bind(('127.0.0.1',8080)) 9 sk.listen() 10 11 while 1: 12 conn,addr = sk.accept() 13 while 1: 14 size = conn.recv(4) #先接受 消息 字節數 15 size = struct.unpack('i',size)[0] 16 print(size) 17 i = 0 18 while 1: 19 with open("recv.txt","ab") as f: 20 if size > 1024: 21 data = conn.recv(1024) 22 else: 23 data = conn.recv(size) 24 size = size - len(data) # 每次不一定 接收的是 1024 ,所以最好用 len(data) 25 f.write(data) 26 i += 1 27 if size <= 0: 28 break 29 print('總次數: ',i) 30 input() 31 conn.close() 32 sk.close()

1 import socket 2 import struct 3 ''' 4 client 端 5 ''' 6 7 sk = socket.socket() 8 9 sk.connect(('127.0.0.1',8080)) 10 11 while 1: 12 with open("test.txt","rb") as f: 13 d = f.read() 14 sk.send(struct.pack('i',len(d))) # 先發送 消息字節數 15 sk.send(d) 16 17 sk.close()
驗證 客戶端的合法性 :
如何生成一個隨機的字節,
os.urandom(32) 可以隨機生成一個32位的隨機字節,
用算法 將它 和 密鑰 在一起生成結果,

1 import os,hashlib 2 r_b = os.urandom(32) 3 4 key = 'towqndsjfkgn%$#@skg&*(ngn)!@sliengah' # 密鑰 服務端 和 客戶端 都有 5 ret = hashlib.md5(key.encode()) 6 ret.update(r_b) 7 res = ret.hexdigest() 8 print(res)

1 import socket 2 import os,hashlib 3 4 key = 'towqndsjfkgn%$#@skg&*(ngn)!@sliengah' 5 6 sk = socket.socket() 7 sk.bind(('127.0.0.1',8080)) 8 sk.listen() 9 10 while 1: 11 #================================ 12 r_b = os.urandom(32) 13 ret = hashlib.md5(key.encode()) 14 ret.update(r_b) 15 res = ret.hexdigest() 16 #================================ 17 conn,addr = sk.accept() 18 conn.send(r_b) 19 recv = conn.recv(1024) 20 if recv.decode() == res: 21 print('合法,開始通信') 22 # 合法 23 while 1: 24 data = conn.recv(1024) 25 print(data) 26 27 ret = input('>>>[server]').encode() 28 conn.send(ret) 29 else: 30 print('不合法,拜拜') 31 # 不合法 關閉連接 32 conn.close()

1 import socket 2 import hashlib 3 4 key = 'towqndsjfkgn%$#@skg&*(ngn)!@sliengah' 5 sk = socket.socket() 6 sk.connect(('127.0.0.1',8080)) 7 8 r_b = sk.recv(1024) 9 # ================================ 10 ret = hashlib.md5(key.encode()) 11 ret.update(r_b) 12 res = ret.hexdigest() 13 # ================================ 14 sk.send(res.encode()) 15 while 1: 16 d = input('>>> [client]') 17 sk.send(d.encode()) 18 19 data = sk.recv(1024) 20 print(data.decode())
Python 的 sockserver 模塊 :
sockserver 和 socket 模塊的關系, 它是基於 socket 的, 而且,它們都是Python 內置的模塊 ,
它幫我們做的事情是: 幫助我們處理 並發的客戶端請求 ,

1 import socketserver 2 3 class MyServer(socketserver.BaseRequestHandler): 4 # 重寫 handle 方法 5 def handle(self): 6 conn = self.request 7 while 1: 8 try: 9 data = conn.recv(1024) 10 print(data.decode()) 11 except ConnectionResetError: 12 break 13 14 server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) 15 server.serve_forever()

1 import socket 2 3 sk = socket.socket() 4 5 sk.connect(('127.0.0.1',8080)) 6 7 while 1: 8 d = input(">>> [client]").encode() 9 sk.send(d)

1 import socket 2 3 sk = socket.socket() 4 5 sk.connect(('127.0.0.1', 8080)) 6 7 while 1: 8 d = input(">>> [client]").encode() 9 sk.send(d)
進程的三種狀態:
就緒, 運行 ,還有阻塞 三個狀態 ,
操作系統 :
我們日常生活中的系統 都是 分時 系統,分時是 分時間片。
也又實時系統, 它是一個cpu 只為一個 程序工作,它一般都是用於 飛機飛行,導彈發射之類的,不能有一分一毫的 差錯,對響應度要求高(不能 分時間片)。
分布式 :
只要是把 大的任務分解為小的任務 都可以稱為分布式,
分布式,可以在操作系統級別實現,也可以在 程序級別實現, Python 的框架: celery
Python 的進程 :
進程 相關的模塊 及函數 :
和進程 相關的 模塊: multiprocessing
from multiprocessing import Processing
os 模塊中的兩個 函數 getpid() 進程id , getppid() 父進程 id
子進程.join() :
只要有子進程.join() ,那么主進程就會阻塞,直到 .join()使用的子進程 執行完畢!
守護進程 :
通過 子進程.daemon = True ,可以將其設置為 守護進程,
該進程 會在 主進程代碼執行徹底完之后 自動結束。
利用 多進程 實現 socket 並發:

1 import socket 2 from multiprocessing import Process 3 4 def commun_with_client(conn): 5 while 1: 6 d = conn.recv(1024) 7 print(d) 8 9 if __name__ == '__main__': 10 sk = socket.socket() 11 sk.bind(('127.0.0.1', 8080)) 12 sk.listen() 13 14 while 1: 15 conn,addr = sk.accept() #不停的 accept, 來一個client 開一個子進程 16 p = Process(target=commun_with_client,args=(conn,)) 17 p.start()

1 import socket 2 sk = socket.socket() 3 4 sk.connect(('127.0.0.1',8080)) 5 6 while 1: 7 d = input(">>> [client]").encode() 8 sk.send(d)

1 import socket 2 sk = socket.socket() 3 4 sk.connect(('127.0.0.1',8080)) 5 6 while 1: 7 d = input(">>> [client]").encode() 8 sk.send(d)
進程 之間的數據共享 --- Manager :
Python 多進程 無法直接獲得 一個函數的 返回值, 但是,可以通過共享變量來實現,
我們可以通過 Manager 去創建 共享變量,它有很多數據類型: list dict Queue 等等,
進程 同步 --- Lock :
為什么要用鎖:

1 from multiprocessing import Process,Manager 2 import time 3 4 def add(d): 5 temp = d['num'] + 1 6 time.sleep(0.001) 7 d['num'] = temp 8 9 if __name__ == '__main__': 10 m = Manager() 11 d = m.dict({'num':0}) 12 p_list = [] 13 for i in range(10): 14 p = Process(target=add,args=(d,)) 15 p_list.append(p) 16 for p in p_list: 17 p.start() 18 19 for p in p_list: 20 p.join() 21 print(d)
下面使用 鎖: 上鎖 .acquire() 下鎖: .release() 。 除了用 .acquire() 和 .release()也可以使用 with 語句,更方便,

1 from multiprocessing import Process,Manager,Lock 2 import time 3 4 def add(d,lock): 5 lock.acquire() 6 temp = d['num'] + 1 7 time.sleep(0.001) 8 d['num'] = temp 9 lock.release() 10 11 if __name__ == '__main__': 12 m = Manager() 13 d = m.dict({'num':0}) 14 lock = Lock() 15 p_list = [] 16 for i in range(10): 17 p = Process(target=add,args=(d,lock)) 18 p_list.append(p) 19 for p in p_list: 20 p.start() 21 22 for p in p_list: 23 p.join() 24 print(d)

1 from multiprocessing import Process,Manager,Lock 2 import time 3 4 def add(d,lock): 5 with lock: # 它會自動 上鎖, 然后執行完代碼 自動解鎖 6 temp = d['num'] + 1 7 time.sleep(0.001) 8 d['num'] = temp 9 10 if __name__ == '__main__': 11 m = Manager() 12 d = m.dict({'num':0}) 13 lock = Lock() 14 p_list = [] 15 for i in range(10): 16 p = Process(target=add,args=(d,lock)) 17 p_list.append(p) 18 for p in p_list: 19 p.start() 20 21 for p in p_list: 22 p.join() 23 print(d)
注: 不能連續 acquire () 傻子也知道,
死鎖 現象:使用多個鎖才會出現的情況,
進程 間通信 --- Queue :
進程間 通信 ( IPC inter process communciate ) 的方式 :
1,基於文件 : 同一台機器上 進程間通信
2,基於網絡 : 同一台 或者 不同電腦 都可
這里的隊列 Queue 就是基於文件 來實現 進程間通信的, 它在 multiprocessing 模塊中,

1 from multiprocessing import Process,Queue 2 import time 3 4 def func(q): 5 q.put('hello') 6 7 def func2(q): 8 time.sleep(1) 9 q.get() 10 11 if __name__ == '__main__': 12 q = Queue() 13 p_list = [] 14 for i in range(10): # 10 個進程 放 到隊列中 15 p = Process(target=func,args=(q,)) 16 p_list.append(p) 17 18 for i in range(5): # 5 個進程 去 從隊列中拿出來 , 最終應該是只剩 5個hello 19 p = Process(target=func2,args=(q,)) 20 p_list.append(p) 21 22 for p in p_list: 23 p.start() 24 25 for p in p_list: 26 p.join() 27 while not q.empty(): 28 print(q.get())
除了上面的基本方法: q.put() q.get() q.empty(),
還有其他方法:
1,get_nowait() :
當隊列為空,時,如果是q.get(),那么程序就會阻塞了, 而使用 get_nowait() 則不會阻塞,但是 它會拋出一個異常 , queue.Empty 異常 (queue 為普通的隊列 )
q.get_nowait() 取不到值時觸發異常:queue.Empty
捕獲這個異常 需要導入import queue(普通的隊列,不是進程間的Queue)

1 from multiprocessing import Process,Queue 2 import time 3 import queue 4 5 def func(q): 6 q.put('hello') 7 8 def func2(q): 9 try: 10 q.get_nowait() 11 except queue.Empty as e: 12 print(e) 13 14 15 if __name__ == '__main__': 16 q = Queue() 17 p_list = [] 18 # for i in range(10): # 10 個進程 放 到隊列中 19 # p = Process(target=func,args=(q,)) 20 # p_list.append(p) 21 22 for i in range(5): # 5 個進程 去 從隊列中拿出來 , 最終應該是只剩 5個hello 23 p = Process(target=func2,args=(q,)) 24 p_list.append(p) 25 26 for p in p_list: 27 p.start() 28 29 for p in p_list: 30 p.join() 31 while not q.empty(): 32 print(q.get())
2,put_nowait() :
與 get_nowait() , 當隊列滿了,此時再put 數據的話,
如果使用q.put(),就阻塞了,直到 q不為空, 如果使用put_nowait() 則不會阻塞,但是,它會拋出異常, queue.Full ,同樣它也在 queue中。

1 from multiprocessing import Process,Queue 2 import time 3 import queue 4 5 def func(q): 6 q.put('hello') 7 8 def func2(q): 9 time.sleep(1) 10 try: 11 q.put_nowait('hello') 12 except queue.Full as e: 13 print('滿了') 14 print(e) 15 16 17 if __name__ == '__main__': 18 q = Queue(10) # 最多10個 19 p_list = [] 20 for i in range(10): # 10 個進程 放 到隊列中 21 p = Process(target=func,args=(q,)) 22 p_list.append(p) 23 24 p = Process(target=func2,args=(q,)) 25 p_list.append(p) 26 27 28 for p in p_list: 29 p.start() 30 31 for p in p_list: 32 p.join() 33 while not q.empty(): 34 print(q.get())
3,full() :
創建隊列時,如果有指明大小則有大小,反之,不限大小。
如果隊列滿了,返回True,反之False;
Queue 的一個子類:JoinableQueue:
JoinableQueue類 ,相對於 Queue 類來說,多了兩個方法 一個是 task_done() ,一個是join()
JoinableQueue 的 put 方法時,Queue 計數器加一,但是 get方法時,不會主動減一,我們調用 task_done()才會減一。
join() 方法時,當計數器為 0 時候,才不阻塞,否則一直阻塞。

1 from multiprocessing import Process,JoinableQueue 2 import time 3 4 def func(q): 5 print('put hello') 6 q.put('hello') 7 8 def func2(q): 9 time.sleep(1) 10 print('get hello') 11 q.get() 12 13 if __name__ == '__main__': 14 q = JoinableQueue(10) # 最多10個 15 p_list = [] 16 for i in range(10): 17 p = Process(target=func,args=(q,)) 18 p_list.append(p) 19 20 for i in range(10): 21 p = Process(target=func2,args=(q,)) 22 p_list.append(p) 23 for p in p_list: 24 p.start() 25 26 time.sleep(1) 27 q.join() # 此時會一直 阻塞 28 print('h') 29 while not q.empty(): 30 print(q.get())
上面之所以 一直阻塞,是因為 沒有調用task_done()

1 from multiprocessing import Process,JoinableQueue 2 import time 3 4 def func(q): 5 print('put hello') 6 q.put('hello') 7 8 def func2(q): 9 time.sleep(1) 10 print('get hello') 11 q.get() 12 q.task_done() 13 14 15 if __name__ == '__main__': 16 q = JoinableQueue(10) # 最多10個 17 p_list = [] 18 for i in range(10): 19 p = Process(target=func,args=(q,)) 20 p_list.append(p) 21 22 for i in range(10): 23 p = Process(target=func2,args=(q,)) 24 p_list.append(p) 25 for p in p_list: 26 p.start() 27 28 time.sleep(1) 29 q.join() # 此時 不會一直阻塞 30 print('h') 31 while not q.empty(): 32 print(q.get())
Python 的線程:
一般處理 並發 都是多線程,不會開啟 多進程,
Python 的GIL 鎖:
它是CPython 解釋器的產物, 它帶來的影響是 多線程 同一時刻 只有一個線程 可以被cpu執行,
這就造成了 Python 中的 多線程 不能利用多核, 即Python中的多線程 是 fake 多線程。
GIL 不會影響我們很多 。
線程 相關的模塊 及函數 :
和線程 相關的 模塊: threading
from threading import Thread
threading 模塊 是模仿着 multiprocessing 來寫的,所以 學習線程就變得 很輕松 ~
線程也又線程id (搜)
多線程用法:
多線程 大部分 和 進程 寫法一致 :

1 from threading import Thread 2 import time 3 4 def func(): 5 time.sleep(1) 6 print('hello world') # 這個時候 就是阻塞的 7 8 if __name__ == '__main__': 9 t_list = [] 10 for i in range(10): 11 t = Thread(target=func) 12 t_list.append(t) 13 14 for t in t_list: # 開啟10 個線程 15 t.start() 16 17 for t in t_list: 18 t.join() 19 20 print('wo shi zhu xiancheng')
與 多進程不同的是:
多線程的 守護線程 守護的是 其他非守護線程(包含 主線程)
多進程的 守護進程 守護的是 主進程。

1 from threading import Thread 2 import time 3 4 def test01(): 5 while True: 6 time.sleep(1) 7 print("我是守護子線程~") 8 9 def test02(): 10 time.sleep(3) 11 print("我是子線程....") 12 13 if __name__ == '__main__': 14 thread = Thread(target=test01) 15 thread.daemon = True 16 thread.start() 17 18 thread2 = Thread(target=test02) 19 thread2.start() 20 print("主線程的代碼結束!") # 證明: 守護線程 守護的是 其他子線程。

1 from threading import Thread 2 import time 3 4 def test01(): 5 while True: 6 time.sleep(1) 7 print("我是守護子線程~") 8 9 def test02(): 10 time.sleep(1) 11 print("我是子線程....") 12 13 if __name__ == '__main__': 14 thread = Thread(target=test01) 15 thread.daemon = True 16 thread.start() 17 18 thread2 = Thread(target=test02) 19 thread2.start() 20 21 time.sleep(10) 22 print("主線程的代碼結束!") # 證明: 守護線程 也守護 主線程。
所以,守護線程 守護的是 其他非守護線程 。
多線程 數據共享:

1 from threading import Thread 2 3 def add(): 4 global num 5 num += 1 6 7 if __name__ == '__main__': 8 num = 0 9 t_list = [] 10 for i in range(100): # 開100個子線程 11 t = Thread(target=add) 12 t_list.append(t) 13 14 for t in t_list: 15 t.start() 16 17 for t in t_list: 18 t.join() 19 print(num)

1 from threading import Thread 2 import time 3 4 def add(): 5 global num 6 temp = num + 1 7 time.sleep(0.001) 8 num = temp 9 10 if __name__ == '__main__': 11 num = 0 12 t_list = [] 13 for i in range(100): # 開100個子線程 14 t = Thread(target=add) 15 t_list.append(t) 16 17 for t in t_list: 18 t.start() 19 20 for t in t_list: 21 t.join() 22 print(num)
下面通過加鎖 解決:

1 from threading import Thread,Lock 2 import time 3 4 def add(lock): 5 global num 6 with lock: 7 temp = num + 1 8 time.sleep(0.001) 9 num = temp 10 11 if __name__ == '__main__': 12 num = 0 13 lock = Lock() 14 t_list = [] 15 for i in range(100): # 開100個子線程 16 t = Thread(target=add,args=(lock,)) 17 t_list.append(t) 18 19 for t in t_list: 20 t.start() 21 22 for t in t_list: 23 t.join() 24 print(num)
多線程 中的隊列:
線程中使用隊列的話,就使用普通的 queue 就行,
threading 中的Timer 函數:
它相當於 js 中的setTimeout()

1 from threading import Timer 2 3 import time 4 5 def func(): 6 print('hello',time.time()) 7 8 9 if __name__ == '__main__': 10 print('hello',time.time()) 11 t = Timer(1,func) 12 t.start()
Python 的 池:
之前 進程 和 線程 都是 有多少個任務 開多少個 進程 /線程, 不好。
如果 先開好 一個數量的 池, 有任務可以直接執行,而且也可以重復利用 池子,減少了 很大的時間開銷 。
池 相關的模塊:
concurrent.futures 模塊
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
進程池:

1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 2 import os,time 3 4 def func(): 5 time.sleep(0.1) 6 print('我是一個任務, 進程id 為: ',os.getpid()) 7 8 def func2(): 9 time.sleep(0.1) 10 print('我是另一個任務, 進程id 為: ',os.getpid()) 11 12 13 if __name__ == '__main__': 14 p_pool = ProcessPoolExecutor(4) # 池中有 4個進程 15 16 for i in range(30): 17 p_pool.submit(func) # 提交10個 func 任務 給 池 18 19 for i in range(15): 20 p_pool.submit(func2) # 提交 5 個func2 任務 給池 21 ''' 22 我是一個任務, 進程id 為: 1320 23 我是一個任務, 進程id 為: 14628 24 我是一個任務, 進程id 為: 18492 25 我是一個任務, 進程id 為: 15896 26 我是一個任務, 進程id 為: 18492 27 我是一個任務, 進程id 為: 14628 28 我是一個任務, 進程id 為: 15896 29 我是一個任務, 進程id 為: 1320 30 我是一個任務, 進程id 為: 1320 31 我是一個任務, 進程id 為: 18492 32 我是一個任務, 進程id 為: 14628 33 我是一個任務, 進程id 為: 15896 34 我是一個任務, 進程id 為: 1320 35 我是一個任務, 進程id 為: 14628 36 我是一個任務, 進程id 為: 18492 37 我是一個任務, 進程id 為: 15896 38 我是一個任務, 進程id 為: 1320 39 我是一個任務, 進程id 為: 18492 40 我是一個任務, 進程id 為: 14628 41 我是一個任務, 進程id 為: 15896 42 我是一個任務, 進程id 為: 15896 43 我是一個任務, 進程id 為: 18492 44 我是一個任務, 進程id 為: 1320 45 我是一個任務, 進程id 為: 14628 46 我是一個任務, 進程id 為: 1320 47 我是一個任務, 進程id 為: 18492 48 我是一個任務, 進程id 為: 14628 49 我是一個任務, 進程id 為: 15896 50 我是另一個任務, 進程id 為: 18492 51 我是一個任務, 進程id 為: 15896 52 我是一個任務, 進程id 為: 1320 53 我是另一個任務, 進程id 為: 14628 54 我是另一個任務, 進程id 為: 18492 55 我是另一個任務, 進程id 為: 14628 56 我是另一個任務, 進程id 為: 1320 57 我是另一個任務, 進程id 為: 15896 58 我是另一個任務, 進程id 為: 15896 59 我是另一個任務, 進程id 為: 18492 60 我是另一個任務, 進程id 為: 1320 61 我是另一個任務, 進程id 為: 14628 62 我是另一個任務, 進程id 為: 15896 63 我是另一個任務, 進程id 為: 18492 64 我是另一個任務, 進程id 為: 1320 65 我是另一個任務, 進程id 為: 14628 66 我是另一個任務, 進程id 為: 15896 67 '''
傳參數:
之前都是 元組,這里直接傳 位置 參數即可,

1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 2 import os,time 3 4 def func(a,b): 5 time.sleep(0.1) 6 print('我是一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 7 8 def func2(a,b): 9 time.sleep(0.1) 10 print('我是另一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 11 12 13 if __name__ == '__main__': 14 p_pool = ProcessPoolExecutor(4) # 池中有 4個進程 15 16 for i in range(10): 17 p_pool.submit(func,i,i+1) # 提交10個 func 任務 給 池 18 19 for i in range(5): 20 p_pool.submit(func2,i,i+1) # 提交 5 個func2 任務 給池 21 22 23 ''' 24 我是一個任務, 進程id 為: 4488 參數: 2 3 25 我是一個任務, 進程id 為: 21540 參數: 3 4 26 我是一個任務, 進程id 為: 12624 參數: 0 1 27 我是一個任務, 進程id 為: 21324 參數: 1 2 28 我是一個任務, 進程id 為: 4488 參數: 5 6 29 我是一個任務, 進程id 為: 12624 參數: 7 8 30 我是一個任務, 進程id 為: 21324 參數: 6 7 31 我是一個任務, 進程id 為: 21540 參數: 4 5 32 我是一個任務, 進程id 為: 12624 參數: 8 9 33 我是另一個任務, 進程id 為: 21324 參數: 0 1 34 我是另一個任務, 進程id 為: 21540 參數: 1 2 35 我是一個任務, 進程id 為: 4488 參數: 9 10 36 我是另一個任務, 進程id 為: 12624 參數: 3 4 37 我是另一個任務, 進程id 為: 21324 參數: 4 5 38 我是另一個任務, 進程id 為: 21540 參數: 2 3 39 '''
獲取返回值:

1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 2 import os,time 3 4 def func(a,b): 5 time.sleep(0.1) 6 print('我是一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 7 return a+b+1 8 9 def func2(a,b): 10 time.sleep(0.1) 11 print('我是另一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 12 return a+b+2 13 14 15 if __name__ == '__main__': 16 p_pool = ProcessPoolExecutor(4) # 池中有 4個進程 17 18 for i in range(10): 19 ret = p_pool.submit(func,i,i+1) # ret 的類型是 Future 對象 可以通過 .result() 獲取(.result() 會阻塞的) 20 print(ret.result()) 21 for i in range(5): 22 ret = p_pool.submit(func2,i,i+1) # ret 的類型是 Future 對象 23 print(ret.result()) 24 25 ''' 26 我是一個任務, 進程id 為: 20516 參數: 0 1 27 2 28 我是一個任務, 進程id 為: 8468 參數: 1 2 29 4 30 我是一個任務, 進程id 為: 4836 參數: 2 3 31 6 32 我是一個任務, 進程id 為: 7648 參數: 3 4 33 8 34 我是一個任務, 進程id 為: 20516 參數: 4 5 35 10 36 我是一個任務, 進程id 為: 8468 參數: 5 6 37 12 38 我是一個任務, 進程id 為: 4836 參數: 6 7 39 14 40 我是一個任務, 進程id 為: 7648 參數: 7 8 41 16 42 我是一個任務, 進程id 為: 20516 參數: 8 9 43 18 44 我是一個任務, 進程id 為: 8468 參數: 9 10 45 20 46 我是另一個任務, 進程id 為: 4836 參數: 0 1 47 3 48 我是另一個任務, 進程id 為: 7648 參數: 1 2 49 5 50 我是另一個任務, 進程id 為: 20516 參數: 2 3 51 7 52 我是另一個任務, 進程id 為: 8468 參數: 3 4 53 9 54 我是另一個任務, 進程id 為: 4836 參數: 4 5 55 11 56 '''
上面 主要是 .result()是個同步 阻塞的過程,
我們可以先加入,后面統一 .result() :

1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 2 import os,time 3 4 def func(a,b): 5 time.sleep(0.1) 6 print('我是一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 7 return a+b+1 8 9 def func2(a,b): 10 time.sleep(0.1) 11 print('我是另一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 12 return a+b+2 13 14 15 if __name__ == '__main__': 16 p_pool = ProcessPoolExecutor(4) # 池中有 4個進程 17 18 res = [] 19 for i in range(10): 20 ret = p_pool.submit(func,i,i+1) # ret 的類型是 Future 對象 可以通過 .result() 獲取(.result() 會阻塞的) 21 res.append(ret) 22 for i in range(5): 23 ret = p_pool.submit(func2,i,i+1) # ret 的類型是 Future 對象 24 res.append(ret) 25 26 27 p_pool.shutdown() # 阻塞, 直到所有任務 結束 28 # 等所有 任務都結束之后 ,再一起看結果 29 for item in res: 30 print(item.result()) # .result() 是同步 , 阻塞的 31 ''' 32 我是一個任務, 進程id 為: 17700 參數: 0 1 33 我是一個任務, 進程id 為: 13092 參數: 2 3 34 我是一個任務, 進程id 為: 9452 參數: 1 2 35 我是一個任務, 進程id 為: 13324 參數: 3 4 36 我是一個任務, 進程id 為: 17700 參數: 4 5 37 我是一個任務, 進程id 為: 13092 參數: 5 6 38 我是一個任務, 進程id 為: 13324 參數: 7 8 39 我是一個任務, 進程id 為: 9452 參數: 6 7 40 我是一個任務, 進程id 為: 17700 參數: 8 9 41 我是另一個任務, 進程id 為: 13092 參數: 0 1 42 我是另一個任務, 進程id 為: 9452 參數: 1 2 43 我是一個任務, 進程id 為: 13324 參數: 9 10 44 我是另一個任務, 進程id 為: 17700 參數: 2 3 45 我是另一個任務, 進程id 為: 13324 參數: 4 5 46 我是另一個任務, 進程id 為: 9452 參數: 3 4 47 2 48 4 49 6 50 8 51 10 52 12 53 14 54 16 55 18 56 20 57 3 58 5 59 7 60 9 61 11 62 '''
map() 方法 :
它是為了 取代 for 循環 submit 這個操作,

1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 2 import os,time 3 4 def func(a): 5 b = a+1 6 time.sleep(0.1) 7 print('我是一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 8 return a+b+1 9 10 def func2(a): 11 b = a+1 12 time.sleep(0.1) 13 print('我是另一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 14 return a+b+2 15 16 17 if __name__ == '__main__': 18 p_pool = ProcessPoolExecutor(4) # 池中有 4個進程 19 20 ret1 = p_pool.map(func,range(10)) # func 為任務函數 ,第二個要求是可迭代對象,為傳入的參數,返回 ret1 為可迭代對象 ,里面放的 直接是 任務函數的返回值 21 ret2 = p_pool.map(func2,range(5)) 22 23 p_pool.shutdown() # 阻塞, 直到所有任務 結束 24 # 等所有 任務都結束之后 ,再一起看結果 25 for item in ret1: 26 print(item) 27 for item in ret2: 28 print(item) 29 30 ''' 31 我是一個任務, 進程id 為: 8972 參數: 0 1 32 我是一個任務, 進程id 為: 15204 參數: 1 2 33 我是一個任務, 進程id 為: 18936 參數: 3 4 34 我是一個任務, 進程id 為: 22288 參數: 2 3 35 我是一個任務, 進程id 為: 8972 參數: 4 5 36 我是一個任務, 進程id 為: 18936 參數: 6 7 37 我是一個任務, 進程id 為: 22288 參數: 7 8 38 我是一個任務, 進程id 為: 15204 參數: 5 6 39 我是一個任務, 進程id 為: 8972 參數: 8 9 40 我是另一個任務, 進程id 為: 18936 參數: 0 1 41 我是另一個任務, 進程id 為: 22288 參數: 1 2 42 我是一個任務, 進程id 為: 15204 參數: 9 10 43 我是另一個任務, 進程id 為: 8972 參數: 2 3 44 我是另一個任務, 進程id 為: 22288 參數: 3 4 45 我是另一個任務, 進程id 為: 15204 參數: 4 5 46 2 47 4 48 6 49 8 50 10 51 12 52 14 53 16 54 18 55 20 56 3 57 5 58 7 59 9 60 11 61 '''
不過 map 傳參時 稍顯復雜,只能傳遞一個 可迭代對象,
add_done_callback(fn):

1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor 2 import os,time 3 4 def func(a,b): 5 time.sleep(0.1) 6 print('我是一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 7 return a+b+1 8 9 def func2(a,b): 10 time.sleep(0.1) 11 print('我是另一個任務, 進程id 為: ',os.getpid(),' 參數:',a,b) 12 return a+b+2 13 14 def ck(ret): 15 print(ret.result()) 16 17 if __name__ == '__main__': 18 p_pool = ProcessPoolExecutor(4) # 池中有 4個進程 19 20 for i in range(10): 21 ret = p_pool.submit(func,i,i+1) 22 ret.add_done_callback(ck) #ret 獲取成功 之后 執行回調 函數 23 for i in range(5): 24 ret = p_pool.submit(func2,i,i+1) 25 ret.add_done_callback(ck) 26 27 ''' 28 我是一個任務, 進程id 為: 14096 參數: 2 3 29 我是一個任務, 進程id 為: 18004 參數: 0 1 30 我是一個任務, 進程id 為: 7292 參數: 1 2 31 我是一個任務, 進程id 為: 20532 參數: 3 4 32 6 33 2 34 4 35 8 36 我是一個任務, 進程id 為: 7292 參數: 5 6 37 我是一個任務, 進程id 為: 14096 參數: 4 5 38 我是一個任務, 進程id 為: 20532 參數: 7 8 39 我是一個任務, 進程id 為: 18004 參數: 6 7 40 12 41 10 42 16 43 14 44 我是一個任務, 進程id 為: 14096 參數: 9 10 45 我是另一個任務, 進程id 為: 18004 參數: 1 2 46 我是另一個任務, 進程id 為: 20532 參數: 0 1 47 我是一個任務, 進程id 為: 7292 參數: 8 9 48 20 49 18 50 3 51 5 52 我是另一個任務, 進程id 為: 14096 參數: 2 3 53 我是另一個任務, 進程id 為: 7292 參數: 3 4 54 我是另一個任務, 進程id 為: 18004 參數: 4 5 55 9 56 7 57 11 58 '''
Python 的協程 Coroutines:
協程 是 操作系統也不可見的,操作系統能調度的最小單位是 線程,
協程 做的工作 是 利用 阻塞的時間,然后執行其他任務,(還是在 一個線程上 )
協程相關的模塊:
兩個 模塊:
1,gevent (第三方) :它是利用了 greenlet (c語言寫)這個模塊 進行 切換任務 ,還有自己內部加的自動規避 io 的功能
2,asyncio :它是利用了 yield(python 的語法)進行 切換任務 , 還有自己內部加的自動規避 io 的功能
協程間 是 數據安全的:
之所以,進程 和 線程 數據不安全,是因為它們都是由 操作系統 控制切換的, 而協程 是 用戶控制切換的。
進程 ,線程,協程 的開銷:
進程 很大
線程 小
協程 更小,(幾乎 和 調用一個函數一樣小)
Python 中 多線程 是否 無用:
通過上述對比,明顯感到 協程 比多線程 好,那么都用協程不就可以了 ,多線程 不用不就行了?
no ,no,no ,
協程有個很大的弊端,它的所有切換都是 基於用戶的 ,只能切換一些 網絡,sleep 等用戶可以感知到的,
但是,像 print, 文件操作 等用戶感知不到的,它是無法切換的,所以只能用 多線程,
所以,多線程 是有用的,
協程 使用:

1 import gevent 2 3 def func1(): 4 print('任務一:開始') 5 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 6 print('任務一:結束') 7 8 if __name__ == '__main__': 9 cor = gevent.spawn(func1) 10 print('主任務') 11 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 12 ''' 13 主任務 14 任務一:開始 15 '''

1 import gevent 2 3 def func1(): 4 print('任務一:開始') 5 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 6 print('任務一:結束') 7 8 if __name__ == '__main__': 9 cor = gevent.spawn(func1) 10 print('主任務') 11 gevent.sleep(2) # 切出去執行 別的沒有阻塞的任務 12 ''' 13 主任務 14 任務一:開始 15 任務一:結束 16 '''
協程.join() 方法:

1 import gevent 2 3 def func1(): 4 print('任務一:開始') 5 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 6 print('任務一:結束') 7 8 if __name__ == '__main__': 9 cor = gevent.spawn(func1) 10 print('主任務') 11 cor.join() 12 print('所有 協程 任務都完成了') 13 14 15 16 ''' 17 主任務 18 任務一:開始 19 任務一:結束 20 所有 協程 任務都完成了 21 '''

1 import gevent 2 3 def func1(): 4 print('任務一:開始') 5 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 6 print('任務一:結束') 7 8 def func2(): 9 print('任務二:開始') 10 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 11 print('任務二:結束') 12 13 14 15 if __name__ == '__main__': 16 cor_list = [] 17 for i in range(10): 18 cor = gevent.spawn(func1) # 10個協程任務 處理 func1 19 cor_list.append(cor) 20 21 for i in range(5): 22 cor = gevent.spawn(func2) # 5個協程任務 處理 func2 23 cor_list.append(cor) 24 25 for cor in cor_list: 26 cor.join() 27 print('所有 協程 任務都完成了') 28 29 30 31 ''' 32 任務一:開始 33 任務一:開始 34 任務一:開始 35 任務一:開始 36 任務一:開始 37 任務一:開始 38 任務一:開始 39 任務一:開始 40 任務一:開始 41 任務一:開始 42 任務二:開始 43 任務二:開始 44 任務二:開始 45 任務二:開始 46 任務二:開始 47 任務一:結束 48 任務一:結束 49 任務一:結束 50 任務一:結束 51 任務一:結束 52 任務一:結束 53 任務一:結束 54 任務一:結束 55 任務一:結束 56 任務一:結束 57 任務二:結束 58 任務二:結束 59 任務二:結束 60 任務二:結束 61 任務二:結束 62 所有 協程 任務都完成了 63 '''
簡便寫法 gevent.joinall() 方法:

1 import gevent 2 3 def func1(): 4 print('任務一:開始') 5 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 6 print('任務一:結束') 7 8 def func2(): 9 print('任務二:開始') 10 gevent.sleep(1) # 切出去執行 別的沒有阻塞的任務 11 print('任務二:結束') 12 13 14 15 if __name__ == '__main__': 16 cor_list = [] 17 for i in range(10): 18 cor = gevent.spawn(func1) # 10個協程任務 處理 func1 19 cor_list.append(cor) 20 21 for i in range(5): 22 cor = gevent.spawn(func2) # 5個協程任務 處理 func2 23 cor_list.append(cor) 24 25 gevent.joinall(cor_list) # gevent.joinall() 更方便 26 print('所有 協程 任務都完成了') 27 28 29 30 ''' 31 任務一:開始 32 任務一:開始 33 任務一:開始 34 任務一:開始 35 任務一:開始 36 任務一:開始 37 任務一:開始 38 任務一:開始 39 任務一:開始 40 任務一:開始 41 任務二:開始 42 任務二:開始 43 任務二:開始 44 任務二:開始 45 任務二:開始 46 任務一:結束 47 任務一:結束 48 任務一:結束 49 任務一:結束 50 任務一:結束 51 任務一:結束 52 任務一:結束 53 任務一:結束 54 任務一:結束 55 任務一:結束 56 任務二:結束 57 任務二:結束 58 任務二:結束 59 任務二:結束 60 任務二:結束 61 所有 協程 任務都完成了 62 '''
協程 傳遞參數:
直接 放入 位置參數即可,
協程 不認是 time.sleep() 類似 問題 :
如何解決:

1 import gevent 2 3 #================================ 4 from gevent import monkey 5 monkey.patch_all() 6 import time 7 #================================ 8 def func1(): 9 print('任務一:開始') 10 time.sleep(1) # 切出去執行 別的沒有阻塞的任務 11 print('任務一:結束') 12 13 def func2(): 14 print('任務二:開始') 15 time.sleep(1) # 切出去執行 別的沒有阻塞的任務 16 print('任務二:結束') 17 18 if __name__ == '__main__': 19 cor_list = [] 20 for i in range(10): 21 cor = gevent.spawn(func1) # 10個協程任務 處理 func1 22 cor_list.append(cor) 23 24 for i in range(5): 25 cor = gevent.spawn(func2) # 5個協程任務 處理 func2 26 cor_list.append(cor) 27 28 gevent.joinall(cor_list) # gevent.joinall() 更方便 29 print('所有 協程 任務都完成了')
使用 gevent 協程實現 socket 並發:

1 import gevent 2 3 #================================ 4 from gevent import monkey 5 monkey.patch_all() 6 import socket 7 #================================ 8 9 def comm_withclient(conn): 10 while 1: 11 d = conn.recv(1024) 12 print(d.decode()) 13 14 15 16 if __name__ == '__main__': 17 sk = socket.socket() 18 sk.bind(('127.0.0.1', 8080)) 19 sk.listen() 20 21 while 1: 22 conn,addr = sk.accept() 23 # 開協程 來一個 客戶端 開一個協程 24 cor = gevent.spawn(comm_withclient,conn)

1 import socket 2 sk = socket.socket() 3 4 sk.connect(('127.0.0.1',8080)) 5 6 while 1: 7 d = input(">>> [client]").encode() 8 sk.send(d)

1 import socket 2 sk = socket.socket() 3 4 sk.connect(('127.0.0.1',8080)) 5 6 while 1: 7 d = input(">>> [client]").encode() 8 sk.send(d)
一台4cpu的電腦 開 5個進程,每個進程開 20個線程,每個線程可以開 500個協程 這一共是:5w 的並發量 (很多了)。
asyncio 模塊 :
參考:https://www.cnblogs.com/Eva-J/articles/10437164.html
它是Python 自帶的一個模塊,

1 import asyncio 2 3 async def func(a): 4 print('開始',a) 5 await asyncio.sleep(1) # 此時會 切換出去執行 沒有阻塞的任務~ 6 print('結束',a+1) 7 8 # await 后面 是個可能會阻塞的方法 9 # await 必須在 async 函數里 10 11 #================================ 12 # 上面操作后, 就不能直接 調一個 協程函數 func 了, | func() × 13 14 evt_loop = asyncio.get_event_loop() 15 # evt_loop.run_until_complete(func(0)) # 這是 開啟一個任務 16 evt_loop.run_until_complete(asyncio.wait([func(1),func(2),func(3)]) ) # 這是 開啟 多個任務
數據庫:
基礎點:
通配符:
% :不限長度,
_:匹配一個字符,
mysql 引擎介紹(存儲引擎):
https://www.cnblogs.com/eva-J/articles/9682063.html
存儲數據的格式,
默認的存儲引擎是 innodb , 它支持 事務(transactions ),行級鎖(row-level locking), 外鍵(foreign keys)
只有 innodb 支持行級鎖,例如myisam 支持的是 表級鎖, (innodb 行級鎖 肯定比 myisam 的表級鎖 效率高 )
各種存儲引擎的使用場景:
一般 默認 innnodb
當只查時, myisam
當數據不是很重要,而且要求速度時:memory ,例如:每個用戶的登錄狀態,
innodb :是一個文件存表結構,一個文件存 數據,
myisam: 三個表,一個文件存結構,一個存數據,還有就是索引,(這也是它快的原因)
memory 和 blackhole :都是只存表結構,
mysql中的數據類型:
https://www.cnblogs.com/eva-J/articles/9683316.html
數值,
1,小數精度要求高的場景(匯率,科研,利息,金融,銀行等),要使用 decimal ,float 和 double 不准,
為什么decimal 准確度:它底層是以 字符串來存的,
不過 decimal 有缺點:就是它
2,整型的約束,沒用,
時間,
date 年月日,
time 時分秒 ,
datetime 年月日時分秒,( 常用 范圍 1000-01-01 ---> 9999-12-31 )
還有timestramp (范圍較小, 1970 - 2038)
平時常用:date , datetime
注:
timestramp 不能為空,默認當前時間,
mysql 中 now() 函數 是當前時間,
字符串,
常用的是 char varchar ,如果要有的話,longtext(存個視頻),但是一般已經不用mysql 存大量數據了,
char : 定長,浪費硬盤,存取速度非常快
varchar:變長,節省空間,存取速度相對慢, (varchar 除了數據以外,要額外存 長度)
char :適合 數據 的長度變化小, 例如手機號,身份證,學號,
varchar:數據長度變化大 ,
枚舉 和 集合 :
枚舉:單選 例如, 性別
集合:多選 例如, 興趣愛好
表的完整性約束:
是否允許空(not null / null ),是否唯一( unique ),主鍵(primary key),外鍵(foreign key),自增(auto)等,
一般是:唯一 非空,
主鍵的特點也是 :唯一,非空,
外鍵:只有innodb 支持外鍵,
連級: cascade , 連級 刪除 ,連級更新,
補: 數據庫的模式很重要,一般要設置為 嚴格模式,
聯合主鍵:
為一個以上的字段 設置 唯一非空,
例如 ip 和 端口 唯一,
create table t (id int, ip char(12) ,port char(10) ,primary key(ip,port ) )
聯合唯一:
unique
create table t(id int primary key ,ip char(12),port char(10) ,unique(ip,port) )
上面ip 和 port 作為主鍵,我們一般習慣 將id 作為主鍵,所以可以用聯合唯一,代替聯合 主鍵:
create table t(id int ,ip char(12) not null ,port char(10) not null ,unique(ip,port) );
注: 不寫主鍵,默認第一個就是,
修改表結構:
https://www.cnblogs.com/eva-J/articles/9677452.html
修改字段名,修改約束 ,修改字段排列順序等,
SQL三種類型:
DDL 語句(數據庫,表,視圖,索引,存儲過程)
DML 語句 (增刪改查)
DCL語句 (權限控制)
記錄操作:
https://www.cnblogs.com/eva-J/articles/9688293.html
主要有 增刪改查:
它是DML ,
查詢:
單表查詢:
https://www.cnblogs.com/Eva-J/articles/9688313.html
多表查詢:
https://www.cnblogs.com/eva-J/articles/9688383.html
表連接:
內連接,
左外連接 (左優先)
右外連接 (右優先)
全外連接 (mysql 中 使用 左 ,右連接, 然后union 關聯下 ),#注意 union與union all的區別:union會去掉相同的記錄
union 的 作用: union查詢就是把2條或者多條sql語句的查詢結果,合並成一個結果集
子查詢:
#1:子查詢是將一個查詢語句嵌套在另一個查詢語句中。 #2:內層查詢語句的查詢結果,可以為外層查詢語句提供查詢條件。 #3:子查詢中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等關鍵字 #4:還可以包含比較運算符:= 、 !=、> 、<等
Python 的 sqlite3 簡單查詢 sqlite3 :
import sqlite3 conn = sqlite3.connect('db.sqlite3') cursor = conn.cursor() ret = cursor.execute('PRAGMA table_info(news_test)') # 表結構信息 print(ret.fetchall()) ret = cursor.execute('select * from news_test') print(ret.fetchall()) cursor.close() conn.close()
pymysql 模塊 的使用:
它是個第三方模塊,
pymysql的基本使用

1 import pymysql 2 conn = pymysql.connect(host='127.0.0.1', 3 user='root', 4 password="123456", 5 database='course',) 6 cursor = conn.cursor() #cursor 游標 7 cursor.execute('show tables;') 8 ret = cursor.fetchall() 9 print(ret) #(('course',), ('dept',), ('dept_temp',), ('students',), ('teacher',)) 10 11 #================================ 12 13 cursor.execute('select * from dept') 14 ret = cursor.fetchall() 15 print(ret) 16 #================================ 17 cursor.execute('select * from dept') 18 ret = cursor.fetchone() 19 print(ret) 20 #================================ 21 22 cursor.execute('select * from dept') 23 ret = cursor.fetchmany(3) 24 print(ret) 25 26 27 #================================ 28 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 此時返回的是 字典~ 29 cursor.execute('select * from dept') 30 ret = cursor.fetchmany(3) 31 print(ret) 32 33 34 cursor.close() # 用完之后 關閉 游標 35 conn.close() # conn 也要斷開連接 36 37 ''' 38 (('course',), ('dept',), ('dept_temp',), ('students',), ('teacher',)) 39 ((1002, 'a'), (1003, 'a'), (1004, '高中部'), (1005, '大學')) 40 (1002, 'a') 41 ((1002, 'a'), (1003, 'a'), (1004, '高中部')) 42 [{'id': 1002, 'dept_name': 'a'}, {'id': 1003, 'dept_name': 'a'}, {'id': 1004, 'dept_name': '高中部'}] 43 '''
pymysql 增 刪 改 (要 commit ):

1 import pymysql 2 conn = pymysql.connect(host='127.0.0.1', 3 user='root', 4 password="123456", 5 database='course',) 6 cursor = conn.cursor() 7 #================================ 8 try: 9 cursor.execute('insert into dept values(1001,"中")') 10 11 12 conn.commit() # 當對 表 增刪改 的時候,都需要 進行 commit 提交 13 except: 14 #如果出現 問題就 進行 回滾 15 print('Q') 16 conn.rollback() 17 18 cursor.close() # 用完之后 關閉 游標 19 conn.close() # conn 也要斷開連接
cursor.rowcount() :

1 import pymysql 2 conn = pymysql.connect(host='127.0.0.1', 3 user='root', 4 password="123456", 5 database='course',) 6 cursor = conn.cursor() 7 #================================ 8 cursor.execute('select * from dept') 9 10 count = cursor.rowcount # 行數 11 12 for i in range(count): 13 print(cursor.fetchone()) 14 15 16 cursor.close() # 用完之后 關閉 游標 17 conn.close() # conn 也要斷開連接
python 代碼登陸 數據庫 (sql 注入 問題) :
如果是用 拼接 參數 來登錄會有下面的問題:
1,是要記得 用戶名 和 密是是雙引號,
2,要注意 -- 代表注釋掉 后面的內容,
3,使用 or 條件
它叫 sql 注入 問題,
我們可以利用 cursor.execute(sql, (user,pwd) ) 來幫我們做,它會防止一些 sql 注入的問題 ,
總結: 一定不能自己拼接 字符串,
python 趣實現:
Python代碼實現進度條:

1 import time 2 for i in range(0,101): 3 time.sleep(0.2) 4 char_num = i # 打印多少個 字符 5 if i == 100: 6 s = '\r{}% : {}\n'.format(i,'*'*char_num) 7 else: 8 s = '\r{}% : {}'.format(i,'*'*char_num) 9 print(s,end='',flush=True)