1. 程式人生 > >python之迭代器、生成器、裝飾器

python之迭代器、生成器、裝飾器

一、迭代器

對於Python 列表的 for 迴圈,他的內部原理:檢視下一個元素是否存在,如果存在,則取出,如果不存在,則報異常 StopIteration。(python內部對異常已處理)

迭代器是訪問集合元素的一種方式。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退,不過這也沒什麼,因為人們很少在迭代途中往後退。另外,迭代器的一大優點是不要求事先準備好整個迭代過程中所有的元素。迭代器僅僅在迭代到某個元素時才計算該元素,而在這之前或之後,元素可以不存在或者被銷燬。這個特點使得它特別適合用於遍歷一些巨大的或是無限的集合,比如幾個G的檔案

特點:

  1. 訪問者不需要關心迭代器內部的結構,僅需通過next()方法不斷去取下一個內容
  2. 不能隨機訪問集合中的某個值 ,只能從頭到尾依次訪問
  3. 訪問到一半時不能往回退
  4. 便於迴圈比較大的資料集合,節省記憶體
class listiterator(object)
 |  Methods defined here:
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  __length_hint__(...)
 |      Private method returning an estimate of len(list(it)).
 |  
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
>>> a = iter([1,2,3,4,5])
>>> a
<list_iterator object at 0x101402630>
>>> a.__next__()
1
>>> a.__next__()
2
>>> a.__next__()
3
>>> a.__next__()
4
>>> a.__next__()
5
>>> a.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

二、生成器

一個函式呼叫時返回一個迭代器,那這個函式就叫做生成器(generator);如果函式中包含yield語法,那這個函式就會變成生成器;

range不是生成器 和 xrange 是生成器

readlines不是生成器 和 xreadlines 是生成器

1

2

3

4

>>> print range(10)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> print xrange(10)

xrange(10)

生成器內部基於yield建立,即:對於生成器只有使用時才建立,從而不避免記憶體浪費

1

2

3

4

5

6

7

8

9

10

11

練習:<br>有如下列表:

    [13, 22, 6, 99, 11]

 

請按照一下規則計算:

13 22 比較,將大的值放在右側,即:[13, 22, 6, 99, 11]

22 6 比較,將大的值放在右側,即:[13, 6, 22, 99, 11]

22 99 比較,將大的值放在右側,即:[13, 6, 22, 99, 11]

99 42 比較,將大的值放在右側,即:[13, 6, 22, 11, 99,]

 

13 6 比較,將大的值放在右側,即:[6, 13, 22, 11, 99,]

...

複製程式碼

li = [13, 22, 6, 99, 11]

for m in range(len(li)-1):

    for n in range(m+1, len(li)):
        if li[m]> li[n]:
            temp = li[n]
            li[n] = li[m]
            li[m] = temp

print li

生成器表示式

1 生成器表示式定義

 

生成器表示式並不真正的建立數字列表,而是返回一個生成器物件,此物件在每次計算出一個條目後,把這個條目"產生"(yield)出來。生成器表示式使用了"惰性計算"或稱作"延時求值"的機制。生成器表示式可以用來處理大資料檔案。

 

序列過長,並且每次只需要獲取一個元素時,應該考慮生成器表示式而不是列表解析。

 

    生成器表示式產生的是一個生成器物件,實質就是迭代器。

 

2 生成器表示式語法

 

語法:

 

  (expression for iter_val in iterable)

 

  (expression for iter_val in iterable if cond_expr)

例:

1

2

3

4

g=("egg%s"%i for i in range(100))

print(g)

print(next(g))

print(next(g))

輸出結果:

1

2

3

<generator object <genexpr> at 0x0000007E9A403D00>

egg0

egg1

 

可以處理大資料檔案:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

f=open("a.txt")

l=[]

for line in f:

    line = line.strip()

    l.append(line)

print(l)

 

f.seek(0)

l1=[line.strip() for line in f]

print(l1)

 

f.seek(0)

g=(line.strip() for line in f)

print(g)

print(next(g))

輸出結果:

1

2

3

4

['wen', 'yan', 'jie']

['wen', 'yan', 'jie']

<generator object <genexpr> at 0x0000000A2B173D00>

wen

 

4、List函式可以處理迭代器和可迭代物件

 

List後面可以跟可迭代物件,和for的實質是一樣的。 List函式將可迭代物件使用iter方法,變成迭代器,然後使用迭代器的next方法遍歷可迭代器的值,並存儲為列表型別,在最後報錯的時候結束。

 

檔案a.txt的內容是

1

2

3

  wen

yan

      jie

程式設計程式碼:  

1

2

3

4

  f=open('a.txt')

g=(line.strip() for line in f)

l=list(g)

print(l)

輸出結果:

1

['wen', 'yan', 'jie']

 

5、sum函式可以處理迭代器和可迭代物件

 

Sum後面可以跟可迭代物件,和sum的實質是一樣的。 Sum函式將可迭代物件使用iter方法,變成迭代器,然後使用迭代器的next方法遍歷可迭代器的值,並,在最後報錯的時候結束。

 

1

2

3

4

5

g=(i for i in range(10))

print(g)

print(sum(g))

print(sum(range(10)))

print(sum([0,1,2,3,4,5,6,7,8,9]))

輸出結果: 

1

2

3

4

<generator object <genexpr> at 0x0000008ED3FA3D00>

45

45

45

Sum中也可以跟可迭代的物件,跟for,list的工作實質型別

1

print(sum([1,2,3,4]))

  

 

6、宣告式程式設計 

 

一種程式設計方式,將需要很多語句的程式碼寫成宣告變數的形式

 

1

g=(line.strip() for line in f)

 

 

7、 生成器表示式舉例

 

在檔案a.txt中的內容:

 

apple 10 3

 

tesla 1000000 1

 

mac 3000 2

 

lenovo 30000 3

 

chicken 10 3

1 計算購買總共的花費:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

以前的做法:

money_l=[]

with open('a.txt') as f:

    for line in f:

        goods=line.split()

        res=float(goods[-1])*float(goods[-2])

        money_l.append(res)

print(money_l)

使用生成器表示式的做法

f=open('a.txt')

g=(float(line.split()[-1])*float(line.split()[-2]) for line in f)

for i in g:

    print(i)

f=open('a.txt')

g=(float(line.split()[-1])*float(line.split()[-2]) for line in f)

print(sum(g))

一句話做法:不要這樣做,python程式碼不是要寫少,而是要寫好,能看懂,且邏輯好

with open('a.txt') as f:

    print(sum(float(line.split()[-1])*float(line.split()[-2]) for line in f))

 

2 將a.txt檔案中的每行內容轉化為字典型別並且儲存到列表

以前做法:

1

2

3

4

5

6

7

8

9

10

res=[]

with open('a.txt') as f:

    for line in f:

        l=line.split()

        d={}

        d["name"]=l[0]

        d["price"]=l[1]

        d["count"]=l[2]

        res.append(d)

print(res)

輸出結果:

1

[{'price': '10', 'name': 'apple', 'count': '3'}, {'price': '1000000', 'name': 'tesla', 'count': '1'}, {'price': '3000', 'name': 'mac', 'count': '2'}, <br>{'price': '30000', 'name': 'lenovo', 'count': '3'}, {'price': '10', 'name': 'chicken', 'count': '3'}]

 

生成器表示式做法

有報錯的:

1

2

3

4

5

6

7

with open('a.txt') as f:

    res=(line.split() for line in f)

    print(res)

    dic_g=({'name':i[0],'price':i[1],'count':i[2]} for i in res)

    print(dic_g)

print(dic_g)

print(next(dic_g))  #原因在於dic_g生成器迭代需要res生成器迭代,res生成器迭代需要f迭代器迭代,f是開啟檔案的控制代碼,一關閉,res生成器和dic_g生成器都不能使用

輸出結果:

1

2

3

4

<generator object <genexpr> at 0x00000044A0DA3D00>

<generator object <genexpr> at 0x00000044A0DA3E08>

<generator object <genexpr> at 0x00000044A0DA3E08>

ValueError: I/O operation on closed file.         #報錯

  

正確生成器做法:

1

2

3

4

5

6

7

with open('a.txt') as f:

    res=(line.split() for line in f)

    print(res)

    dic_g=({'name':i[0],'price':i[1],'count':i[2]} for i in res)

    print(dic_g)

    apple_dic=next(dic_g)

    print(apple_dic["count"])

輸出結果:

1

2

3

<generator object <genexpr> at 0x00000081D5243D00>

<generator object <genexpr> at 0x00000081D5243E08>

3

  

3 將a.txt檔案中的每行內容轉化為字典型別並且取出單價大於10000的商品儲存到列表,

生成器表示式呼叫生成器表示式

1

2

3

4

5

6

7

with open('a.txt') as f:

    res=(line.split() for line in f)

    print(res)

    dic_g=({'name':i[0],'price':i[1],'count':i[2]} for i in res if float(i[1]) >10000)

    print(dic_g)

    for i in dic_g:

        print(i)

輸出結果:

1

2

3

4

<generator object <genexpr> at 0x000000DB4C633D00>

<generator object <genexpr> at 0x000000DB4C633DB0>

{'price': '1000000', 'count': '1', 'name': 'tesla'}

{'price': '30000', 'count': '3', 'name': 'lenovo'}

 

1

2

3

4

5

6

with open('a.txt') as f:

    res=(line.split() for line in f)

    print(res)

    dic_g=({'name':i[0],'price':i[1],'count':i[2]} for i in res if float(i[1]) >10000)

    print(dic_g)

print(list(dic_g))

輸出結果:

1

2

3

<generator object <genexpr> at 0x00000099A0953D00>

<generator object <genexpr> at 0x00000099A0953DB0>

[{'price': '1000000', 'name': 'tesla', 'count': '1'}, {'price': '30000', 'name': 'lenovo', 'count': '3'}]

 

今日作業

(1)有兩個列表,分別存放來老男孩報名學習linux和python課程的學生名字

linux=['鋼彈','小壁虎','小虎比','alex','wupeiqi','yuanhao']

python=['dragon','鋼彈','zhejiangF4','小虎比']

問題一:得出既報名linux又報名python的學生列表

1

2

3

4

5

6

linux=['鋼彈', '小壁虎', '小虎比', 'alex', 'wupeiqi', 'yuanhao']

python=['dragon', '鋼彈', 'zhejiangF4', '小虎比']

li=[i for i in linux for j in python if i==j]

print(li)

li=(i for i in linux for j in python if i==j)

print(list(li))

問題二:得出只報名linux,而沒有報名python的學生列表

1

2

3

4

li=[ i for i in linux if i not in python]

print(li)

li=(i for i in linux if i not in python)

print(list(li))

問題三:得出只報名python,而沒有報名linux的學生列表

1

2

3

4

li=[i for i in python if i not in linux]

print(li)

li=(i for i in python if i not in linux)

print(list(li))

 

(2)

         shares={

         'IBM':36.6,

         'lenovo':27.3,

         'huawei':40.3,

         'oldboy':3.2,

         'ocean':20.1

      }

問題一:得出股票價格大於30的股票名字列表

1

2

li=( i for i,j in shares.items() if j > 30)

print(list(li))

問題二:求出所有股票的總價格

1

2

3

li=(float(j) for j in shares.values())

print(sum(li))

print(sum(float(j) for j in shares.values()))

  

(3)

l=[10,2,3,4,5,6,7]

得到一個新列表l1,新列表中每個元素是l中對應每個元素值的平方。過濾出l1中大於40的值,然後求和

1

2

3

4

5

l = [10, 2, 3, 4, 5, 6, 7]

l1=[i**2 for i in l]

print(l1)

l2=[i for i in l1 if i >40]

print(sum(l2))

1、什麼是裝飾器

 

裝飾器本質上是一個python函式,它可以讓其他函式在不需要做任何程式碼變動的前提下增加額外功能。

 

裝飾器的返回值是也是一個函式物件。

 

 裝飾器經常用於有切面需求的場景,比如:插入日誌,效能測試,事務處理、快取、許可權校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函式功能無關的雷同程式碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。

 

 為什麼要用裝飾器及開放封閉原則

 

 函式的原始碼和呼叫方式一般不修改,但是還需要擴充套件功能的話就需要在需要擴充套件的函式的開始使用裝飾器。舉例:帶眼鏡

 

裝飾器是任意可呼叫的物件,本質就是函式 

 

裝飾器在python中使用如此方便歸因於python的函式能像普通的物件一樣能作為引數傳遞給其他函式,可以被複制給其他變數,可以作為返回值,可以被定義在另一個函式內。

 

2、 簡單的裝飾器

1

2

3

4

5

6

7

8

9

10

11

12

13

import time

def timmer(func):

    def wrapper(*args,**kwargs):

        start_time=time.time()

        res=func(*args,**kwargs)

        stop_time=time.time()

        print("run time is %s "%(stop_time-start_time))

    return wrapper

@timmer            #等同於 index=timmer(index) , 此後index等同於 wrapper  

def index():

    time.sleep(1)

    print("welcom ! wen")

index()

輸出結果:

1

2

welcom ! wen

run time is 1.0001096725463867

 

函式timmer就是裝飾器,它把執行真正業務方法的func包裹在函式裡面,看起來像index被timmer裝飾了。

在這個例子中,函式進入和退出時,被稱為一個橫切面(Aspet),這種程式設計方式被稱為面向切面的程式設計(Aspet-Oriented Programming)

@符號是裝飾器的語法糖,在定義函式的時候,避免再一次賦值操作。

 

3、裝飾器的語法

 

@timmer  

 

timmer就是一個裝飾器

 

@timmer等同於 被裝飾函式名=timmer(被裝飾函式名)   被裝飾器函式就是緊接@timmer下面的函式

 

 

4、無參裝飾器

 

如果多個函式擁有不同的引數形式,怎麼共用同樣的裝飾器?

 

在Python中,函式可以支援(*args, **kwargs)可變引數,所以裝飾器可以通過可變引數形式來實現內嵌函式的簽名。

 

無參裝飾器,被裝飾函式帶參,無返回值

1

2

3

4

5

6

7

8

9

10

import time

def timmer(func):

    def wrapper(*args,**kwargs):

        print(func)

    return wrapper

@timmer

def index():

    time.sleep(1)

    print("welcom ! wen")

index()

輸出結果:

1

<function index at 0x000000D132F5AF28>

 

多個函式共用一個裝飾器:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

import time

def timmer(func):

    def wrapper(*args,**kwargs):

        start_time=time.time()

        func(*args,**kwargs)

        stop_time=time.time()

        print("run time is: %s" %(stop_time-start_time))

    return wrapper

@timmer

def home(name):

    time.sleep(1)

    print(" %s  home"%name)

@timmer

def auth(name,password):

    time.sleep(1)

print(name,password)

@timmer

def tell():

    time.sleep(1)

    print("------------------")

home("wenyanjie")

auth("wen","1234")

tell()

輸出結果為:

 wenyanjie  home

run time is: 1.0002663135528564

wen 1234

run time is: 1.0000183582305908

------------------

run time is: 1.0009756088256836

  

無參裝飾器,被裝飾函式帶參,且有返回值

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

import time

def timmer(func):

    def wrapper(*args,**kwargs):

        start_time=time.time()

        res=func(*args,**kwargs)

        stop_time=time.time()

        print("run time is: %s" %(stop_time-start_time))

        return res

    return wrapper

 

@timmer

def home(name):

    time.sleep(1)

    print(" %s  home"%name)

@timmer

def auth(name,password):

    time.sleep(1)

    print(name,password)

@timmer

def tell():

    time.sleep(1)

    print("------------------")

@timmer

def my_max(x,y):

    return x if x > y else y

 

home("wenyanjie")

auth("wen","1234")

tell()

res=my_max(1,2)

print("----->",res)

輸出結果:

1

2

3

4

5

6

7

8

wenyanjie  home

run time is: 1.0004072189331055

wen 1234

run time is: 1.0009665489196777

------------------

run time is: 1.0001206398010254

run time is: 0.0

-----> 2

  

5、有參裝飾器 

裝飾器還有更大的靈活性,例如帶引數的裝飾器:在上面的裝飾器呼叫中,比如@timmer,該裝飾器唯一的引數是執行業務的函式。裝飾器的語法允許我們在呼叫時,提供其他引數。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

def auth2(auth_type):

    def auth(func):

        def wrapper(*args,**kwargs):

            if auth_type == "file":

                name=input("username:")

                password=input("password")

                if name=="wen" and password =="123":

                    print("login successful!")

                    res=func(*args,**kwargs)

                    return res

                else:

                    print("auth error")

            elif auth_type == "sql":

                print("sql no learn.....")

        return wrapper

    return auth

 

@auth2(auth_type="file")

def login(name):

    print("this is %s index page"%name)

    return 1

flag=login("wenyanjie")

print(flag)

輸出結果:

1

2

3

4

5

username:wen

password123

login successful!

this is wenyanjie index page

1

上面例子中的auth2是允許帶引數的裝飾器。它實際上是對原有裝飾器的一個函式封裝,並返回一個裝飾器。我們可以將他理解為一個含有引數的閉包。當我們使用@auth2(auth_type=”file”)呼叫的時候,python能夠發現這一層的封裝,並把引數傳遞到裝飾器的環境中。

 

上面有參裝飾器的執行步驟分解:

 

6、多個裝飾器

裝飾器是可以疊加的,那麼這就涉及裝飾器呼叫順序。對於python中的“@”語法糖,裝飾器的呼叫順序與使用@語法糖的順序相反。

 

多個無參裝飾器

1

2

3

4

5

@ccc

@bbb

@aaa

def func():

    pass

#相當與

1

2

3

func=aaa(func)

func=bbb(func)

func=ccc(func)

#相當與

1

func=ccc(bbb(aaa(func)))

 

有參裝飾器多個

1

2

3

4

5

@ccc('c')

@bbb('b')

@aaa('c')

def func():

    pass

#相當與

1

2

3

func=aaa('a')(func)

func=bbb('b')(func)

func=ccc('c')(func)

#相當與

1

func=ccc('c')(bbb('b')(aaa('a')(func)))

  

案例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

import time

current_login={'name':None,'login':False}

def timmer(func):

    def wrapper(*args,**kwargs):

        start_time=time.time()

        res=func(*args,**kwargs)

        stop_time=time.time()

        print("run time is %s"%(stop_time-start_time))

        return res

    return wrapper

def auth2(auth_type):

    def auth(func):

        def wrapper(*args,**kwargs):

            if current_login['name'] and current_login['login']:

                res=func(*args,**kwargs)

                return res

            if auth_type == "file":

                name=input("username:")

                password=input("password:")

                if name=="wen" and password =="123":

                    print("login successful!")

                    current_login['name']=name

                    current_login['login']=password

                    res=func(*args,**kwargs)

                    return res

                else:

                    print("auth error")

            elif auth_type == "sql":

                print("sql no learn.....")

        return wrapper

    return auth

@timmer

@auth2(auth_type="file")

def login(name):

    print("this is %s index page"%name)

login("wenyanjie")

login("wenyanjie")

輸出結果:

1

2

3

4

5

6

7

username:wen

Password:123

login successful!

this is wenyanjie index page

run time is 3.228583812713623

this is wenyanjie index page

run time is 0.0

 

7  eval方法:往檔案中放有結構的資料 

 

 

8 被裝飾函式將所有註釋等自己的定義都傳給裝飾器 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import time

from functools import wraps

def timmer(func):

    @wraps(func)

    def wrapper(*args,**kwargs):

        "wrapper function"

        print(func)

        start_time=time.time()

        res=func(*args,**kwargs)

        stop_time=time.time()

        print("run time is %s "%(stop_time-start_time))

        return res

    return wrapper

@timmer

def my_max(x,y):

    "my_max function"

    a=x if x>y else y

    return a

print(help(my_max))

 

輸出結果:

1

2

my_max(x, y)

    my_max function

http://www.cnblogs.com/wupeiqi/articles/4943406.html

https://www.cnblogs.com/wupeiqi/articles/4938499.html

https://www.cnblogs.com/wupeiqi/articles/5433893.html