歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Python自動(dòng)化運(yùn)維之高級(jí)函數(shù)

發(fā)布時(shí)間:2017-08-08 15:32  回復(fù):0  查看:2329   最后回復(fù):2017-08-08 15:32  
本文和大家分享的主要是python自動(dòng)化運(yùn)維中高級(jí)函數(shù)相關(guān)內(nèi)容,一起來看看吧,希望對(duì)大家學(xué)習(xí)python有所幫助。
  一、協(xié)程
  1.1協(xié)程的概念
  協(xié)程,又稱微線程,纖程。英文名Coroutine。一句話說明什么是線程:協(xié)程是一種用戶態(tài)的輕量級(jí)線程。(其實(shí)并沒有說明白~
  那么這么來理解協(xié)程比較容易:
  線程是系統(tǒng)級(jí)別的,它們是由操作系統(tǒng)調(diào)度;協(xié)程是程序級(jí)別的,由程序員根據(jù)需要自己調(diào)度。我們把一個(gè)線程中的一個(gè)個(gè)函數(shù)叫做子程序,那么子程序在執(zhí)行過程中可以中斷去執(zhí)行別的子程序;別的子程序也可以中斷回來繼續(xù)執(zhí)行之前的子程序,這就是協(xié)程。也就是說同一線程下的一段代碼執(zhí)行著執(zhí)行著就可以中斷,然后跳去執(zhí)行另一段代碼,當(dāng)再次回來執(zhí)行代碼塊的時(shí)候,接著從之前中斷的地方開始執(zhí)行。
  比較專業(yè)的理解是:
  協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧。因此:協(xié)程能保留上一次調(diào)用時(shí)的狀態(tài)(即所有局部狀態(tài)的一個(gè)特定組合),每次過程重入時(shí),就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài),換種說法:進(jìn)入上一次離開時(shí)所處邏輯流的位置。
  1.2 協(xié)程的優(yōu)缺點(diǎn)
  協(xié)程的優(yōu)點(diǎn):
 ?。?/span>1)無需線程上下文切換的開銷,協(xié)程避免了無意義的調(diào)度,由此可以提高性能(但也因此,程序員必須自己承擔(dān)調(diào)度的責(zé)任,同時(shí),協(xié)程也失去了標(biāo)準(zhǔn)線程使用多CPU的能力)
 ?。?/span>2)無需原子操作鎖定及同步的開銷
 ?。?/span>3)方便切換控制流,簡(jiǎn)化編程模型
  (4)高并發(fā)+高擴(kuò)展性+低成本:一個(gè)CPU支持上萬的協(xié)程都不是問題。所以很適合用于高并發(fā)處理。
  協(xié)程的缺點(diǎn):
 ?。?/span>1)無法利用多核資源:協(xié)程的本質(zhì)是個(gè)單線程,它不能同時(shí)將 單個(gè)CPU 的多個(gè)核用上,協(xié)程需要和進(jìn)程配合才能運(yùn)行在多CPU.當(dāng)然我們?nèi)粘K帉懙慕^大部分應(yīng)用都沒有這個(gè)必要,除非是cpu密集型應(yīng)用。
 ?。?/span>2)進(jìn)行阻塞(Blocking)操作(如IO時(shí))會(huì)阻塞掉整個(gè)程序
  2、Python中如何實(shí)現(xiàn)協(xié)程
  2.1 yield實(shí)現(xiàn)協(xié)程
  前文所述子程序(函數(shù))在執(zhí)行過程中可以中斷去執(zhí)行別的子程序;別的子程序也可以中斷回來繼續(xù)執(zhí)行之前的子程序,那么很容易想到Pythonyield,顯然yield是可以實(shí)現(xiàn)這種切換的。
  def eater(name):
  print("%s eat food" %name)
  while True:
  food = yield
  print("done")
  g = eater("gangdan")
  print(g)
  執(zhí)行結(jié)果:
  <generator object eater at 0x0000000002140FC0>
  由執(zhí)行結(jié)果可以證明g現(xiàn)在就是生成器函數(shù)
  2.2 協(xié)程函數(shù)賦值過程
  用的是yield的表達(dá)式形式,要先運(yùn)行next(),讓函數(shù)初始化并停在yield,然后再send() ,send會(huì)在觸發(fā)下一次代碼的執(zhí)行時(shí),給yield賦值
  next()send() 都是讓函數(shù)在上次暫停的位置繼續(xù)運(yùn)行,
  def creater(name):
  print('%s start to eat food' %name)
  food_list = []
  while True:
  food = yield food_list
  print('%s get %s ,to start eat' %(name,food))
  food_list.append(food)# 獲取生成器
  builder = creater('tom')# 現(xiàn)在是運(yùn)行函數(shù),讓函數(shù)初始化
  next(builder)
  print(builder.send('包子'))
  print(builder.send('骨頭'))
  print(builder.send('菜湯'))
  執(zhí)行結(jié)果:
  tom start to eat food
  tom get 包子 ,to start eat
  ['包子
  tom get 骨頭 ,to start eat
  ['包子', '骨頭
  tom get 菜湯 ,to start eat
  ['包子', '骨頭', '菜湯
  需要注意的是每次都需要先運(yùn)行next()函數(shù),讓程序停留在yield位置。
  如果有多個(gè)這樣的函數(shù)都需要執(zhí)行next()函數(shù),讓程序停留在yield位置。為了防止忘記初始化next操作,需要用到裝飾器來解決此問題
  def init(func):
  def wrapper(*args,**kwargs):
  builder = func(*args,**kwargs)
  next(builder)    # 這個(gè)地方是關(guān)鍵可以使用builder.send("None"),第一次必須傳入None。
  return builder
  return wrapper@initdef creater(name):
  print('%s start to eat food' %name)
  food_list = []
  while True:
  food = yield food_list
  print('%s get %s ,to start eat' %(name,food))
  food_list.append(food)# 獲取生成器
  builder = creater("tom")# 現(xiàn)在是直接運(yùn)行函數(shù),無須再函數(shù)初始化
  print(builder.send('包子'))
  print(builder.send('骨頭'))
  print(builder.send('菜湯'))
  執(zhí)行結(jié)果:
  tom start to eat food
  tom get 包子 ,to start eat
  ['包子
  tom get 骨頭 ,to start eat
  ['包子', '骨頭
  tom get 菜湯 ,to start eat
  ['包子', '骨頭', '菜湯
  2.3 協(xié)程函數(shù)簡(jiǎn)單應(yīng)用
  請(qǐng)給Tom投喂食物
  def init(func):
  def wrapper(*args,**kwargs):
  builder = func(*args,**kwargs)
  next(builder)
  return builder
  return wrapper@initdef creater(name):
  print('%s start to eat food' %name)
  food_list = []
  while True:
  food = yield food_list
  print('%s get %s ,to start eat' %(name,food))
  food_list.append(food)def food():
  builder = creater("Tom")
  while True:
  food = input("請(qǐng)給Tom投喂食物:").strip()
  if food == "q":
  print("投喂結(jié)束")
  return 0
  else:
  builder.send(food)if __name__ == '__main__':
  food()
  執(zhí)行結(jié)果:
  Tom start to eat food
  請(qǐng)給Tom投喂食物:骨頭
  Tom get 骨頭 ,to start eat
  請(qǐng)給Tom投喂食物:菜湯
  Tom get 菜湯 ,to start eat
  請(qǐng)給Tom投喂食物:q
  投喂結(jié)束
  2.4 協(xié)程函數(shù)的應(yīng)用
  實(shí)現(xiàn)linux"grep -rl error <目錄>"命令,過濾一個(gè)文件下的子文件、字文件夾的內(nèi)容中的相應(yīng)的內(nèi)容的功能程序
  首先了解一個(gè)OS模塊中的walk方法,能夠把參數(shù)中的路徑下的文件夾打開并返回一個(gè)元組
  >>> import os # 導(dǎo)入模塊>>> os.walk(r"E:\Python\script") #使用是讓字符串中的符號(hào)沒有特殊意義,針對(duì)的是轉(zhuǎn)義
  >>> g = os.walk(r"E:\Python\script")>>> next(g)
  ('E:\\Python\\script', ['.idea', '函數(shù)'], [])
  返回的是一個(gè)元組,第一個(gè)元素是文件的路徑,第二個(gè)是文件夾,第三個(gè)是該路徑下的文件
  這里需要用到一個(gè)寫程序的思想:面向過程編程
  二、面向過程編程
  面向過程:核心是過程二字,過程及即解決問題的步驟,基于面向過程設(shè)計(jì)程序就是一條工業(yè)流水線,是一種機(jī)械式的思維方式。流水線式的編程思想,在設(shè)計(jì)程序時(shí),需要把整個(gè)流程設(shè)計(jì)出來
  優(yōu)點(diǎn):
  1:體系結(jié)構(gòu)更加清晰
  2:簡(jiǎn)化程序的復(fù)雜度
  缺點(diǎn):
  可擴(kuò)展性極其的差,所以說面向過程的應(yīng)用場(chǎng)景是:不需要經(jīng)常變化的軟件,如:linux內(nèi)核,httpd,git等軟件
  下面就根據(jù)面向過程的思想完成協(xié)程函數(shù)應(yīng)用中的功能
  目錄結(jié)構(gòu):
  test
  ├── aa
  │   ├── bb1
  │    │    └── file2.txt
  │   └── bb2
  │       └── file3.txt
  └─ file1.txt
  文件內(nèi)容:file1.txterror123file2.txt123file3.txt123error
  程序流程
  第一階段:找到所有文件的絕對(duì)路徑
  第二階段:打開文件
  第三階段:循環(huán)讀取每一行
  第四階段:過濾“error”
  第五階段:打印該行屬于的文件名
  第一階段:找到所有文件的絕對(duì)路徑
  g是一個(gè)生成器,就能夠用next()執(zhí)行,每次next就是運(yùn)行一次,這里的運(yùn)行結(jié)果是依次打開文件的路徑
  >>> import os>>> g = os.walk(r"E:\Python\script\函數(shù)\test")>>> next(g)
  ('E:\\Python\\script\\函數(shù)\\test', ['aa'], [])>>> next(g)
  ('E:\\Python\\script\\函數(shù)\\test\\aa', ['bb1', 'bb2'], ['file1.txt'])>>> next(g)
  ('E:\\Python\\script\\函數(shù)\\test\\aa\\bb1', [], ['file2.txt'])>>> next(g)
  ('E:\\Python\\script\\函數(shù)\\test\\aa\\bb2', [], ['file3.txt'])>>> next(g)
  Traceback (most recent call last):
  File "file:///C:\Users\wlc\AppData\Local\Temp\ksohtml\wps86F5.tmp.wmf", line 1, in
  StopIteration
  我們?cè)诖蜷_文件的時(shí)候需要找到文件的絕對(duì)路徑,現(xiàn)在可以通過字符串拼接的方法把第一部分和第三部分進(jìn)行拼接
  用循環(huán)打開:
  import os
  dir_g = os.walk(r"E:\Python\script\函數(shù)\test")for dir_path in dir_g:
  print(dir_path)
  結(jié)果:
  ('E:\\Python\\script\\函數(shù)\\test', ['aa'], [])
  ('E:\\Python\\script\\函數(shù)\\test\\aa', ['bb1', 'bb2'], ['file1.txt'])
  ('E:\\Python\\script\\函數(shù)\\test\\aa\\bb1', [], ['file2.txt'])
  ('E:\\Python\\script\\函數(shù)\\test\\aa\\bb2', [], ['file3.txt'])
  將查詢出來的文件和路徑進(jìn)行拼接,拼接成絕對(duì)路徑
  import os
  dir_g = os.walk(r"E:\Python\script\函數(shù)\test")for dir_path in dir_g:
  for file in dir_path[2]:
  file = "%s\\%s" %(dir_path[0],file)
  print(file)
  執(zhí)行結(jié)果:
  E:\Python\script\函數(shù)\test\aa\file1.txtE:\Python\script\函數(shù)\test\aa\bb1\file2.txtE:\Python\script\函數(shù)\test\aa\bb2\file3.txt
  用函數(shù)實(shí)現(xiàn):
  import osdef search():
  while True:
  dir_name = yield
  dir_g = os.walk(dir_name)
  for dir_path in dir_g:
  for file in dir_path[2]:
  file = "%s\\%s" %(dir_path[0],file)
  print(file)
  g = search()
  next(g)
  g.send(r"E:\Python\script\函數(shù)\test")
  為了把結(jié)果返回給下一流程
  @init   # 初始化生成器def search(target):
  while True:
  dir_name = yield
  dir_g = os.walk(dir_name)
  for pardir,_,files in dir_g:
  for file in files:
  abspath = r"%s\%s" %(pardir,file)
  target.send(abspath)
  第二階段:打開文件
  @initdef opener(target):
  while True:
  abspath=yield
  with open(abspath,'rb') as f:
  target.send((abspath,f))
  第三階段:循環(huán)讀出每一行內(nèi)容
  @initdef cat(target):
  while True:
  abspath,f=yield #(abspath,f)
  for line in f:
  res=target.send((abspath,line))
  if res:break
  第四階段:過濾
  @initdef grep(pattern,target):
  tag=False
  while True:
  abspath,line=yield tag
  tag=False
  if pattern in line:
  target.send(abspath)
  tag=True
  第五階段:打印該行屬于的文件名
  @initdef printer():
  while True:
  abspath=yield
  print(abspath)
  g = search(opener(cat(grep('error'.encode('utf-8'), printer()))))
  g.send(r'E:\Python\script\函數(shù)\test')
  執(zhí)行結(jié)果:
E:\Python\script\函數(shù)\test\aa\file1.txt
E:\Python\script\函數(shù)\test\aa\bb2\file3.txt

來源:51CTO
您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?