Python的重要知識點匯總1


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()
高階函數 -- map()
 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()
高階函數 -- filter()

 

 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()

注: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())
View Code

 

它在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 就終止了生成器
生成器函數 里也可以有 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)
yield 和 yield from 的區別
 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 和 yield from

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)
@test()

 

多個裝飾器 裝飾同一個函數:

 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 '''
View Code

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 '''
View Code

參看: 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)
View Code

 

 

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()
datetime 模塊
 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)
datetime 模塊 中timedelta用法

 

 

 

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) ")
View Code

 

如何同時 輸出到 屏幕 和 文件?

這時要用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()
class 和 type兩種方式定義類,都可以

 

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)
View Code

 

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
View Code

 

 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] 
通過__setattr__ 來控制屬性的類型 和 范圍

 

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
View Code

 

總結:

__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']
View Code

 

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

 

再看 @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)
@property 的完整運用

 

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__ + 類屬性 實現單例模式

其實,默認 調用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 '''
View Code

 

 

網絡通信:

粘包 現象:

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()
server.py
 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()
client.py

但是,上面代碼里 最多接受字節的長度是 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))
View Code

 

所以,我們可以將我們的 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()
server.py
 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()
client.py

 

 

大文件 多次傳輸 :

 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()
server.py
 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()
client.py

 

驗證 客戶端的合法性 :

如何生成一個隨機的字節, 

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()
server.py
 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())
client.py

 

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()
server.py
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)
client1.py
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)
client2.py

 

 

 

進程的三種狀態:  

就緒, 運行  ,還有阻塞 三個狀態 ,   

 

操作系統 :  

我們日常生活中的系統 都是 分時 系統,分時是 分時間片。  

也又實時系統, 它是一個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()
server.py
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)
client1.py
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)
client2.py

 

 

進程 之間的數據共享 --- 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())
進程間 隊列 Queue 的基本使用

 

除了上面的基本方法: 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())
View Code

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())
View Code

 

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')
View Code

 

與 多進程不同的是:

多線程的 守護線程 守護的是 其他非守護線程(包含 主線程)

多進程的 守護進程 守護的是 主進程。

 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()
View Code

 

 

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 '''
View Code

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 函數

不過 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
 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 '''
基本用法 2

 

協程.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 '''
使用 協程.join() 來阻塞 主任務
 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 '''
協程.join()

 

簡便寫法 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 '''
gevent.joinall()

協程 傳遞參數: 

直接 放入 位置參數即可,

 

協程  不認是 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('所有 協程 任務都完成了')
View Code

 

 

使用 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)
server.py
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)
client1.py
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)
client2.py

一台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)]) )  # 這是 開啟 多個任務
async 和 await 關鍵字

 

數據庫:

基礎點:

通配符:

% :不限長度,

_:匹配一個字符,  

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 也要斷開連接
View Code

 

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 也要斷開連接
View Code

 

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)
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM