簡學Python第四章__裝飾器、迭代器、列表生成式

Python第四章__裝飾器、迭代器

歡迎加入Linux_Python學習羣python

  羣號:478616847web

 

 

目錄:面試

  • 列表生成式redis

  • 生成器算法

  • 迭代器數據庫

  • 單層裝飾器(無參)編程

  • 多層裝飾器(有參)數據結構

  • 冒泡算法閉包

  • 代碼開發規範app

 

1、列表生成式(列表推導式

  列表生成式List Comprehensions,是Python內置的很是簡單卻強大的能夠用來建立list的生成式。

  首先來上個需求,我有一個列表 [1,2,3,4,5,6,7,8,9,10],如今有這麼個需求,要裏面的元素自乘,想一想看要怎麼實現

  版本一,經過for循環,從新賦值(佔內存空間)

1 a = [1,2,3,4,5,6,7,8,9,10]
2 b = []
3 for i in a:
4     b.append(i*i)
5 a = b
6 print(a)
版本一

  版本二,經過for循環修改原值(代碼太多)

1 a = [1,2,3,4,5,6,7,8,9,10]
2 for indexs,i in enumerate(a):
3     a[indexs] *= i
4 print(a)
版本二

  版本三,藉助map和lambda(代碼也很多)

1 a = [1,2,3,4,5,6,7,8,9,10]
2 a = map(lambda x:x*x,a)
3 print(list(a))
版本三

  列表生成式(Python的高級特性),一句話便可實現上面的功能,在列表生成式中咱們能夠加if判斷,也能夠加多個for循環,多個for循環,咱們就把它

  想成for循環嵌套,而且實際結果也是for循環嵌套的結果

 1 a = [i*i  for i in range(1,11)]
 2 print(a)
 3 
 4 #加if判斷
 5 a = [i*i  for i in range(1,11) if i%2 == 1 ]
 6 print(a)
 7 
 8 #多層for循環
 9 a = [a+b+c for a in "abc" for b in "ABC" for c in "123"]
10 print(a)
11 
12 
13 #多層for循環解析
14 s = []
15 for a in "abc" :
16     for b in "ABC":
17         for c in "123":
18             s.append(a+b+c)
19 print(s)
列表生成式

 

2、生成器

  生成器也叫簡單生成器,它是能夠簡單有效的建立出迭代器的工具,而且經過yield語句,當每次對有yield語句函數使用next()的時候

  生成器會從yield中止的位置繼續開始

  生成器的特性:

  一、生成器是用普通的函數語法定義的迭代器

  二、經過next()語句調用,生成器函數將從yield語句處繼續執行

  三、節省空間,生成器只是指定的計算方式,在不調動的時候不會生成全部的值 

  四、經過生成器的這種能夠再次從yield語句處再次執行的特性,咱們能夠完成協同程序(下面例子說明)

  協同程序:協同程序是能夠運行的獨立函數調用,能夠暫停或者掛起,並從程序離開的地方繼續或者從新開始。

 

  第一個簡單生成器

  下面的代碼主要是定義一個函數,這個函數接收一個數字的參數,而後經過list(range(num))生成一個列表。並for循環這個列表

  每次循環都觸發 yield i ,當第一次循環觸發yield i 則程序會中止到這裏而且產生一個值,等待下一步激活,那麼經過__next__()next()

  就去到yield 返回的值,而且從中止的位置繼續執行,知道再碰到yield,或者執行完成。

  

 1 def Out_num(num):
 2     for i in list(range(num)):
 3         yield i
 4 
 5 Builder = Out_num(5)
 6 
 7 #返回的不是一個數字而是一個迭代器
 8 print(Builder)
 9 
10 #取值方式
11 print("我是經過__next__()取值:",Builder.__next__())
12 print("我是經過next()取值:",next(Builder))
13 
14 #for循環取值
15 print("我是經過for循環取值↓")
16 for i in Builder:
17     print(i)
生成器

  要注意的一點是經過__next__()next()取值時當生成器中沒有值而你取值則程序會報錯,經過for循環取值時不存在這個問題的!

 1 >>> def Out_num(num):
 2 ...     for i in list(range(num)):
 3 ...         yield i
 4 ...
 5 >>> Builder = Out_num(3)
 6 
 7 >>> Builder.__next__()
 8 0
 9 >>> Builder.__next__()
10 1
11 >>> Builder.__next__()
12 2
13 >>> Builder.__next__()
14 Traceback (most recent call last):
15   File "<stdin>", line 1, in <module>
16 StopIteration
生成器報錯

 

  協同程序與send()

  咱們知道了協同程序就是程序能夠暫停或者掛起,而且能夠從程序離開的地方繼續或者從新開始

  下面的例子是個吃包子與作包子的模擬程序,經過yield讓consumer函數暫停,並執行作包子的操做,作完包子後經過send(),給yield賦值,

  而且繼續執行consumer函數中的代碼,並且咱們能夠經過send()傳進來的值判斷作什麼操做,就象例子中「梅菜肉餡的包子」同樣,隔壁老王不吃而隔壁老李愛吃。

 1 import time
 2 def consumer(name):
 3     print("%s 準備吃包子啦!" %name)
 4     while True:
 5         baozi = yield
 6         if baozi == "牛肉" or baozi == "酸菜":
 7             print("%s餡的包子來了,%s愛吃,被[%s]吃了!" %(baozi,name,name))
 8         elif baozi == "梅菜肉" and name == "隔壁老李":
 9             print("%s餡的包子來了,%s愛吃,被[%s]吃了!" %(baozi,name,name))
10         else:
11             print("%s餡的包子很差吃,%s不吃!" %(baozi,name))
12 
13 def producer():
14     c = consumer('隔壁老王')
15     c2 = consumer('隔壁老李')
16     c.__next__()
17     c2.__next__()
18     print("來顧客了,大王開始作包子了!")
19     species = ["牛肉","素三鮮","酸菜","梅菜肉","韭菜"]
20     for i in species:
21         time.sleep(1)
22         print("---------------------分割線-------------------------")
23         print("作了%s餡的包子"%i)
24         c.send(i)
25         c2.send(i)
26 producer()
協同程序(只吃好吃的包子)

 

   生成器表達式

  在上面學習了列表生成式,和生成器,那麼生成器表達式它和列表生成式的用法基本一致,其工做方式是每次處理一個對象,而不是一口氣處理和構造整個數據結構,

  也就是返回的是一個可迭代的對象,這樣作的潛在優勢是能夠節省大量的內存,

  語法:(expr for iter_var in iterable) 或 (expr for iter_var in iterable if cond_expr)

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 #生成器表達式
 5 c = (x for x in "ABCDEFG")
 6 print(c)
 7 #列表生成式
 8 a = [i*i  for i in range(1,11) if i%2 == 1 ]
 9 print(a)
10 
11 lists = ["user","pass","age","gender"]
12 s = {i:"" for i in lists }
13 print(s)
生成器表達式

 

3、迭代器

  什麼是迭代器?一種是能夠直接做用於for循環的數據類型,另外一種就是有一個 next() 方法的對象, 而不是經過索引來計數,也就說生成器生成的就是迭代器,

  迭代器也有一些限制. 例如你不能向後移動,不能回到開始, 也不能複製一個迭代器,可使用isinstance()判斷一個對象是不是可迭代:

1 from collections import Iterable
2 print(isinstance([],Iterable))
3 print(isinstance({},Iterable))
4 print(isinstance((),Iterable))
5 print(isinstance("abcd",Iterable))
6 #數字沒法迭代
7 print(isinstance(100,Iterable))
判斷對象是否可迭代

   生成器都是迭代器,可是雖然 list dict str能夠迭代,可是它們不是迭代器,經過iter()函數能夠把可迭代的對象變成迭代器

1 from collections import Iterator
2 print(isinstance([],Iterator))
3 print(isinstance({},Iterator))
4 print(isinstance((),Iterator))
5 print(isinstance("abcd",Iterator))
6 
7 print(isinstance((x for x in range(10)),Iterator))
判斷是不是迭代器

  你可能會問,爲何list、dict、str等數據類型不是Iterator(迭代器)?

  這是由於Python的Iterator對象表示的是一個數據流,Iterator對象能夠被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。

  能夠把這個數據流看作是一個有序序列,但咱們卻不能提早知道序列的長度,只能不斷經過next()函數實現按需計算下一個數據,因此Iterator的計算是惰性

  的,只有在須要返回下一個數據時它纔會計算。Iterator甚至能夠表示一個無限大的數據流,例如全體天然數。而使用list是永遠不可能存儲全體天然數的。

 

4、單層裝飾器(無參)

  裝飾器是函數式編程的重中之重!學好裝飾器不只能讓你的代碼看上去,B格更高,也能體現出技術含量,更能實現開發封閉原則,裝飾器的背後主要動機源自

  python 面向對象編程。因此裝飾器是在函數調用之上的修飾。也就是說裝飾器的做用是在不動函數源碼的前提上給函數加功能,抽象的理解就是把函數裝飾起

  來,是函數執行前與執行後都能作不一樣的操做!

   上故事有一家公司,準備面試新人,這個部門的老大親自當面試官,而且呢,給面試者出了這麼一道題

  咱們有這麼一段代碼,這段代碼,這段代碼假設是N個業務部門的業務的函數,這段代碼的意思就是當咱們調用上面的函數的時候,傳入值給arg,

  當arg的值等於f1或者f2那麼對應的函數就返回ok

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 def f1(arg):
 5     print('我是F1業務')
 6     if arg == 'f1':
 7         return 'ok'
 8 
 9 def f2(arg):
10     print('我是F2業務')
11     if arg == 'f2':
12         return 'ok'
業務代碼

  那麼公司有N個業務部門,1個基礎平臺部門,基礎平臺負責提供底層的功能,如:數據庫操做、redis調用、監控API等功能。業務部門使用基礎功能時,

  只需調用基礎平臺提供的功能便可。那麼咱們業務部門調用功能的時候只須要:

    f1(值)

    f2(值)

  然而呢我發現了一個問題就是業務部調用基礎平臺的功能的時候沒有驗證這樣很差,因此請各位面試者把驗證功能加上,而且業務部門在調用功能的方式不能變

  應聘者LowA

  他是這麼作的,他說跟各個作基礎功能的人協調,要求在本身業務的代碼上加入驗證模塊,那麼這樣呢整個的基礎平臺就不須要更改,結果,老大直接請他走了

  應聘者LowB

  這個LowB看到LowA直接被趕出去了,內心煩了低估,一樣的題LowB把本身每一個基礎代碼函數裏面都加上了驗證代碼,老大一看,隨便問了幾句,也讓他回去等消息了

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 def f1(arg):
 5     #驗證代碼
 6     #驗證代碼
 7     print('我是F1業務')
 8     if arg == 'f1':
 9         return 'ok'
10 
11 def f2(arg):
12     #驗證代碼
13     #驗證代碼
14     print('我是F2業務')
15     if arg == 'f2':
16         return 'ok'
LowB

  應聘者LowC

  他看到了這道題,他把驗證代碼單獨寫成一個函數,而後在基礎函數中調用這個驗證函數,老大看見了LowC的實現方式,嘴角露出了一絲微笑,而且與LowC聊了個天

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 #驗證函數
 5 def verify():
 6     # 驗證1
 7     # 驗證2
 8     # 驗證3
 9     pass
10 
11 def f1(arg):
12     verify()
13     print('我是F1業務')
14     if arg == 'f1':
15         return 'ok'
16 
17 def f2(arg):
18     verify()
19     print('我是F2業務')
20     if arg == 'f2':
21         return 'ok'
LowC

  老大說:

  寫代碼要遵循開發封閉原則,雖然在這個原則是用的面向對象開發,可是也適用於函數式編程,簡單來講,它規定已經實現的功能代碼不容許被修改,但能夠被擴展,即:

  封閉:已實現的功能代碼塊

  開放:對擴展開發

  若是將開放封閉原則應用在上述需求中,那麼就不容許在函數 f1 、f2的內部進行修改代碼,老闆就給了Low C一個實現方案並說何時看懂了何時來上班工資20K:

  ❤單層裝飾器(無參)

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 #裝飾器函數
 5 def func(main):
 6     def wra(*args,**kwargs):
 7         #驗證代碼
 8         return main(*args,**kwargs)
 9     return wra
10 
11 #python語法糖  @func  =>  f1 = func(f1)
12 @func
13 def f1(arg):
14     print('我是F1業務')
15     if arg == 'f1':
16         return 'ok'
17 
18 def f2(arg):
19     print('我是F2業務')
20     if arg == 'f2':
21         return 'ok'
22 
23 c = f1("f1")
24 print(c)       
單層裝飾器(無參裝飾器)

 

  剖析 第一步:

  

  首先@func 是python裝飾器的語法糖,這個語句是放在被修飾函數的上方,當出現這個語句就要把它當作   f1 = func(f1) 

  公式  被裝飾函數= 裝飾器函數 (被裝飾函數) 能夠看出被裝飾函數此時變成參數被裝飾器函數傳了進去,因此裝飾器函數

  (func)的參數main就是 被裝飾函數 f1,因此main = f1,而且咱們在第三章知道了函數不加括號是不執行的,因此main

  就是函數 f1的內存地址,且不會執行。

  第二步:

  

  咱們發現,裝飾器函數func 就是在第三章學的閉包,因此當@func的時候 就是 f1 = func(f1) ,咱們要知道函數加上括號就會執行

  因此func就執行了,而且由於func是個閉包函數,因此把內部wra函數加載到內存,並返回,因此此時 f1 = wra,而且要注意的

  是返回的是wra 沒有加括號哦!

  第三步:

  

  當看到 c = f1("f1")的時候,由於python語法糖的緣由因此f1就是 wra函數也就是圖中紅色標記箭頭的地方,因此f1("f1")就是執行

  了wra函數,而且把參數「f1」傳了進去,第三章學過動態參數,因此被裝飾的函數傳入什麼參數咱們的wra都不須要改,而後看wra函

  數內部作了個return main(*args,**kwargs)第一步的時候咱們得出告終論 main就是f1函數,因此 main(*args,**kwargs)

  ,就是執行了f1函數而且把 c = f1("f1") 中的參數「f1」傳到的真正的函數 f1裏也就是藍色框圈起來的內容,而且第三章咱們也知道了,

  傳參的時候用 *args,**kwargs 就是把列表和字典分解(不理解請參考第三章解參)。

  最後:main(*args,**kwargs) 就是執行了f1函數,當f1函數執行完成後,就return 「ok」,因此wra中的 return實際返回的就是

  f1函數返回的結果,這也就是c爲何等於「ok」固然就算f1函數沒有返回值也無所謂,由於默認會返回None。到此無參裝飾器剖析完畢

  

5、多層裝飾器(有參)

  咳咳,通過LowC的認真學習,他終於明白了無參裝飾器的真意,因而成功的入職了公司,可是隨着時間的推移,老大又找到了Low C說
  這樣我有個需求要你給我改改,我如今呢想在驗證以後呢添加一個歡迎功能,這個功能呢,咱們業務線的功能想要添加就添加先要不
  添加就不添加,要記住封閉原則哦0.0……….

  次日Low C找到了老大說,大哥啊您晚上仍是來我家教教我吧,真心的不知道啊0.0,,,因而老大就去了Low C的家裏通過一場風雲
   (此處省略一萬個字),最後老大提供了另外的參考代碼:

  ❤多層裝飾器(有參)

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 #歡迎函數
 5 def welcome():
 6     print("歡迎訪問")
 7 
 8 #裝飾器函數
 9 def fill(*func):
10     def single_func(main):
11         def wra(*args,**kwargs):
12             #驗證代碼
13             if len(func) !=0:
14                 for i in func:
15                     i()
16             return main(*args,**kwargs)
17         return wra
18     return single_func
19 
20 @fill(welcome)#python語法糖  @fill(welcome)  => @single_func =>  f1 = func(f1)
21 def f1(arg):
22     print('我是F1業務')
23     if arg == 'f1':
24         return '我是f1 ok'
25     
26 @fill()
27 def f2(arg):
28     print('我是F2業務')
29     if arg == 'f2':
30         return '我是f2 ok'
31 
32 c = f1("f1")
33 print(c)
34 print("------------分割線----------")
35 c2 = f2("f2")
36 print(c2)
多層裝飾器

   

  剖析:第一步

  

   其實有參裝飾器,就是無參裝飾器加了個閉包,這個閉包的參數接收一個函數,而且使用*func表示這個參數就表示能夠傳參也能夠不傳參

  @fill(welcome) 咱們知道函數加上括號就會執行,因此先執行了裝飾器函數fill,而且把welcome函數傳給了參數*func,而後咱們把subgle_func

  當成一個總體,那麼這就是個閉包結構,因此當fill()執行後的到了  @fill(welcome) == @single_func   *func == welcome這樣咱們在閉包

  內部就能夠調用 welcome這個函數!

 

  第二步:

  

  在第一步中獲得了 @fill(welcome) == @single_func  那麼這就變成了無參裝飾器,那麼就跟上面講到的同樣 @single_func => f1 = single_func(f1)

  因此 f1 = wra函數 main = f1 函數

 

  第三步:

  

  當 f1("f1") 的時候就開始執行wra函數,在wra函數內部咱們要先執行*func內的函數,畢竟歡迎信息在前面,因此先執行

  而且咱們同if判斷能夠判斷使用者是否傳入了函數,傳入就執行,不傳入就不執行,執行完成*func內的函數後就製成執行main

  函數(main = f1函數),也就是執行了f1,到此需求實現。

 

6、冒泡算法

  接下來學習一下冒泡排序,冒泡排序的主要思路就是從第一個元素開始,判斷這個元素與每個元素的大小關係,當符合判斷關係

  後就進行位置替換

 1 def bubble(lists):
 2     count = len(lists)
 3     for i in range(0, count-1):
 4         for j in range(i + 1, count):
 5             if lists[i] > lists[j]:
 6                 lists[i], lists[j] = lists[j], lists[i]
 7             print("第%s次排序"%(i+1),lists)
 8     return lists
 9 
10 print([5,2,9,1,7,6,8,3,4])
11 print(bubble([5,2,9,1,7,6,8,3,4]))
冒泡排序

  上面的例子中有兩層for循環,最內層for循環的做用是比較第一個值與其它每一個值的大小,若是大則替換位置,而後拿替換後的值接着、

  跟剩下的值進行比較,同理若是大則替換位置,當這一次循環完成後就用第二個值與第二個值後面的值進行比較,若是大則替換位置,

  直到只有最後一個值的時候排序就完成了,例子中的列表,讓每一個元素進行大小值的比較須要循環八次也就是最後是後面兩個元素比較

  就能夠了。

  

 

7、代碼開發規範

  隨着Python在國內的發展,使用python編程語言的公司愈來愈多,在使用python進行開發的時候,不只僅只是編寫代碼時要注意規範

  並且隨着程序的複雜,在格式上也須要有規範,這樣你的程序在別人眼中的可讀性會大大增長,下面一塊兒來看看編寫代碼時要注意的事情

  縮進

  在python代碼中縮進的重要是毋庸置疑的,縮進錯誤會形成程序錯誤,而且好的縮進在代碼可讀性上也很是有用

  註釋

  註釋是程序很是重要的東西,無論是爲了其餘人閱讀你的代碼,仍是爲了你本身,由於時間久了不少你本身寫的程序代碼本身都會看不懂

  這個時候要快速理解本身的代碼,註釋就尤其重要,因此你的每個函數,和後面會學到的類,以及類中的方法,都要給他們加上註釋

  行的最大長度

  每行代碼或者輸出內容若是過長在顯示上和美觀上都有較大的影響,因此當一行代碼或者內容過長時建議使用  \ 進行換號,推薦將長度限

  制在72字符。

  軟件目錄結構

  "設計項目目錄結構",就和"代碼編碼風格"同樣,屬於我的風格問題。目錄規範,能更好的控制程序結構,讓程序具備更高的可讀性,關於如

  何組織一個較好的Python工程目錄結構,已經有一些獲得了共識的目錄結構。在Stackoverflow的這個問題上,能看到你們對Python目錄結

  構的討論,因此一個程序的目錄設計我以爲應該有如下設計,假設你的項目名爲foo,最後說一句,目錄結構只是更好的讓程序有更好的可讀

  性和易維護性,因此目錄結構並不是一塵不變,理解其中的意思便可。

  

 

 

 

 

 

做者:北京小遠
出處:http://www.cnblogs.com/bj-xy/  本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索