Python - 三大器 迭代器,生層器,裝飾器

Python - 三大器 迭代器,生層器,裝飾器

在介紹三大器以前先來了解一下容器和可迭代對象...python

一. 容器

容器是一種把多個元素組織在一塊兒的數據結構,容器中的元素能夠逐個地迭代獲取,能夠用in, not in關鍵字判斷元素是否包含在容器中。一般這類數據結構把全部的元素存儲在內存中(也有一些特例,並非全部的元素都放在內存,好比迭代器和生成器對象)在Python中,常見的容器對象有:數組

  • list, deque...
  • set, frozensets(不可變集合)...
  • dict, defaultdict, OrderedDict, Counter...
  • tuple, namedtuple...
  • str

容器的概念就像一個盒子,能夠往裏面裝東西.當它能夠用來詢問某個元素是否包含在其中時,那麼這個對象就能夠認爲是一個容器,好比 list,set,tuples都是容器對象:數據結構

>>> assert 1 in [1, 2, 3]      # lists
>>> assert 4 not in [1, 2, 3]
>>> assert 1 in {1, 2, 3}      # sets
>>> assert 4 not in {1, 2, 3}
>>> assert 1 in (1, 2, 3)      # tuples
>>> assert 4 not in (1, 2, 3)

詢問某元素是否在dict中用dict的中key:閉包

>>> d = {1: 'foo', 2: 'bar', 3: 'qux'}
>>> assert 1 in d
>>> assert 'foo' not in d  # 'foo' 不是dict中的元素
詢問某substring是否在string中:
>>> s = 'foobar'
>>> assert 'b' in s
>>> assert 'x' not in s
>>> assert 'foo' in s

儘管絕大多數容器都提供了某種方式來獲取其中的每個元素,但這並非容器自己提供的能力,而是可迭代對象賦予了容器這種能力,固然並非全部的容器都是可迭代的,好比:Bloom filter,雖然Bloom filter能夠用來檢測某個元素是否包含在容器中,可是並不能從容器中獲取其中的每個值,由於Bloom filter壓根就沒把元素存儲在容器中,而是經過一個散列函數映射成一個值保存在數組中。app

二. 可迭代對象(iterable)

大部分對象都是可迭代,只要實現了__iter__方法的對象就是可迭代的。
__iter__方法會返回迭代器(iterator)自己,例如:函數

>>> lst = [1,2,3]
>>> lst.__iter__()
<listiterator object at 0x7f97c549aa50>

Python提供一些語句和關鍵字用於訪問可迭代對象的元素,好比for循環、列表解析、邏輯操做符等。工具

判斷一個對象是不是可迭代對象:spa

>>> from collections import Iterable  # 只導入Iterable方法
>>> isinstance('abc', Iterable)     
True
>>> isinstance(1, Iterable)     
False
>>> isinstance([], Iterable)
True

這裏的isinstance()函數用於判斷對象類型。
可迭代對象通常都用for循環遍歷元素,也就是能用for循環的對象均可稱爲可迭代對象。
例如,遍歷列表:code

>>> lst = [1, 2, 3]
>>> for i in lst:
...   print i
...

三. 迭代器

迭代器協議是指:對象必須提供一個next方法,執行該方法要麼返回迭代中的下一項,要麼就引發一個Stoplteration異常,以終止迭代(只能日後走不能往前退)

實現了迭代器協議的對象(對象內部定義了一個__iter__()方法)

python中的內部工具(如for循環,sum,min,max函數等)基於迭代器協議訪問對象。

使用迭代器的好處:

1)省內存,若是使用列表,計算值時會一次獲取全部值,那麼就會佔用更多的內存。而迭代器則是一個接一個計算,只能向前,不能反覆

2)使代碼更通用、更簡單。

3)  惰性機制

判斷是不是迭代器:

>>> from collections import Iterator
>>> isinstance(d, Iterator)
False
>>> isinstance(d.iteritems(), Iterator)
True

使用next方法:

>>> iter_items = d.iteritems()
>>> iter_items.next()
('a', 1)
>>> iter_items.next()
('c', 3)
>>> iter_items.next()
('b', 2)

迭代器的原理:

1 #基於迭代器協議
 2 li = [1,2,3]
 3 diedai_l = li.__iter__()
 4 print(diedai_l.__next__())
 5 print(diedai_l.__next__())
 6 print(diedai_l.__next__())
 7 # print(diedai_l.__next__())  # 超出邊界報錯
 8 
 9 #下標
10 print(li[0])
11 print(li[1])
12 print(li[2])
13 # print(li[3]) # 超出邊境報錯
14 
15 # 用while循環模擬for循環機制
16 diedai_l = li.__iter__()
17 while True:
18     try:
19         print(diedai_l.__next__())
20     except StopIteration:
21         print("迭代完畢,循環終止")
22         break
23 
24 # for循環訪問方式
25 # for循環本質就是遵循迭代器協議的訪問方式,先調用diedai_l=li.__iter__方法
26 # 或者直接diedai_l=iter(l),而後依次執行diedai_l.__next__(),直到捕捉到
27 # StopItearation終止循環
28 # for循環全部的對象的本質都是同樣的原理

四. 生成器

能夠理解爲一種數據類型,自動實現迭代器協議

  在調用生成器運行的過程當中,每次遇到yield時函數會暫停並保存當前全部的運行信息,返回yield的值。並在下一次執行next()方法時從當前位置繼續運行

  1. 表現形式:

    生成器函數 帶yield的函數(一、返回值 二、保留函數的運行狀態)

          next(t)  t.__next__(能夠拿到數據)  t.send(能夠拿到數據,能夠給上一層的yield傳值)

     # 用生成器函數
     # yield 至關於return控制的是函數的返回值
     # x=yield的另一個特性,接收send傳過來的值,賦值給x
     def test():
         print("開始啦")
         first = yield # return 1   first = None
         print("第一次",first)
         yield 2
         print("第二次")
     t = test()
     print(test().__next__())
     res = t.__next__() # next(t)
     print(res)
     res = t.send("函數停留在first那個位置,我就是給first賦值的")
     print(res)
    
     輸出結果
     開始啦
     None
     開始啦
     None
     第一次 函數停留在first那個位置,我就是給first賦值的
  2. 推導式
    列表推導式:
            [結果 for循環 if語句]
    字典推導式:
            {key:value for if}
    集合推導式
            {key for if}
    
            沒有元組推導式!!!!!!!

     

  3. 生成器表達式  (結果 for if)

    print(sum(i for i in range(10000))) # 表達式通常用for循環 (i for i in range(10000))
    # 做用 節省內存,在內部已經實現了__iter__的方法

五. 裝飾器

1. 定義

在不修改被修飾函數的源代碼和調用方式的狀況下給其添加額外功能.

開閉原則:
開放: 能夠對軟件添加新的功能
封閉: 不能夠修改源代碼

裝飾器 = 高階函數+函數嵌套+閉包

通用裝飾器

def wrapper(fn): # fn是目標函數
            def inner(*args, **kwargs):
                '''執行以前'''
                ret = fn(*args, **kwargs) # 執行目標函數
                '''執行以後'''
                return ret
            return inner

        @wrapper
        def func():
            pass

        func()  # inner()
帶有參數的裝飾器
        def wrapper_out(參數):
            def wrapper(fn):
                def inner(*args, **kwargs):
                    '''在以前'''
                    ret = fn(*args, **kwargs)
                    '''在以後'''
                    return ret
                return inner
            return wrapper

        @wrapper_out(實參)
        同一個函數被多個裝飾器裝飾

 

import time #導入時間模塊

def foo(func):  # func = test
    def bar(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)   # 這裏就是在運行test()  賦予變量
        stop_time = time.tiem()
        print("一場LOL時間爲{}").format(stop_time-start_time)
        return res  # 返回test()的值
    return bar

def test(name, age):
    time.sleep(1)
    print("哈哈")
    return "heihei"

res = test("德瑪西亞", age=10)
print(ret)

六. 閉包

根據這句話,其實咱們本身就能夠總結出在python語言中造成閉包的三個條件,缺一不可:

1)必須有一個內嵌函數(函數裏定義的函數)——這對應函數之間的嵌套

2)內嵌函數必須引用一個定義在閉合範圍內(外部函數裏)的變量——內部函數引用外部變量

3)外部函數必須返回內嵌函數——必須返回那個內部函數

def funx():
x=5
def funy():
    nonlocal x
    x+=1
    return x
return funy

python閉包的優勢:

避免使用全局變量

能夠提供部分數據的隱藏

能夠提供更優雅的面向對象實現

相關文章
相關標籤/搜索