閉包,迭代器

1、函數名的運用(第一類對象)html

       函數名是一個變量,但它是一個特殊的變量,與括號配合能夠執行函數的變量。python

1,函數名的內存地址;網絡

ef func():
        print("呵呵")
    print(func)
    # 結果爲:<function func at 0x00000000029299D8>   打印出func的內存地址

2,函數名能夠賦值給其餘變量;  閉包

def func():
        print("呵呵")
    
    a = func   # 把函數當成一個變量賦值給另外一個變量
    a()    # 函數調用  func()

3,函數名能夠當作容器類的元素; 函數

def func1():
        print("呵呵")
    def func2():
        print("呵呵")
    def func3():
        print("呵呵")
    lst = [func1 , func2 , func3]
    for i in lst:
        i()

4,函數名能夠當作函數的參數;url

def func():
        print("吃了麼")

    def func2(fn):
        print("我是func2")
        fn()    # 執行傳遞過來的fn
        print("我是func2")

    func2(func)   # 把函數名func當成參數傳遞給func2的參數fn

5,函數名能夠做爲函數的返回值;  spa

def func_1():
        print("這裏是函數1")
        def func_2():
            print("這裏是函數2")
        print("這裏是函數1")
        return func_2
    fn = func_1()
    # 執行函數1,函數1返回的是函數2,這時fn指向的就是上面函數2
    # 結果爲:
    # 這裏是函數1
    # 這裏是函數1
    fn()   # 執行上面返回的函數2
    # 結果爲:
    # 這裏是函數2

2、閉包翻譯

什麼是閉包?閉包就是內層函數對外層函數(非全局)的變量的引用,以下示例:htm

 

def func():
        a = 10
        def inner():
            print(a)    # 閉包
        inner()
    func()   # 結果爲:10

咱們能夠用__closure__來檢測函數是否是閉包,函數名.__closure__返回cell就是閉包,返回None就不是閉包。  對象

def func():
        a = 10
        def inner():
            print(a)
        inner()
        print(inner.__closure__)  # (<cell at 0x00000000025875B8: int object at 0x0000000058E26D40>,)
    func()

 問題:如何在函數外邊調用內部函數呢?具體示例以下: 

def outer():
        a = 'hello'
        # 內部函數
        def inner():
            print(a)
        return inner

    fn = outer()    # 訪問外部函數,獲取到內部函數的內存地址
    fn()   # 訪問內部函數

那若是多層嵌套呢?很簡單,只須要一層一層的往外層返回就好了,具體示例以下:  

def func1():
        def func2():
            def func3():
                print("嘿嘿")
            return func3
        return func2
    func1()()()   # 執行func3,結果爲:嘿嘿

由它咱們能夠引出閉包的好處,因爲咱們在外界能夠訪問內部函數,那這個時候內部函數訪問的時間和時機就不必定了,由於在外部,我能夠選擇在任意的時間去訪問內部函數,這個時候,想想,咱們以前說過,若是一個函數執行完畢,則這個函數中的變量以及局部命名空間中內容將會被銷燬,在閉包中,若是變量被銷燬了,那內部函數將不能正常執行,因此,python規定:若是你在內部函數中訪問了外層函數中的變量,那麼這個變量將不會消亡,將會常駐在內存中,也就是說,使用閉包,能夠保證外層函數中的變量在內存中常駐。這樣作有什麼好處呢?有很是大的好處,下面來看一個簡單的關於爬蟲的代碼:  

 

from urllib.request import urlopen

    def but():
        content = urlopen("http://www.xiaohua100.cn/index.html").read()
        def get_content():
            return content
        return get_content

    fn = but()  # 這個時候就開始加載校花100的內容
    # 後面須要用到這裏面的內容就不須要再執行很是耗時的網絡鏈接操做了
    content = fn()   # 獲取內容
    print(content)

    content2 = fn()   # 從新獲取內容
    print(content2)

  

綜上可得:閉包的做用就是讓一個變量可以常駐內存,供後面的程序使用。

閉包補充:有以下代碼示例:

def func():
        a = "hello"
        def inner():
            print(a)
        return inner

    func()()  # 耗時,由於想要執行內層函數inner就要先執行一遍外層函數func,沒有用到閉包的好處:外層函數變量常駐內存

    fn = func()  # 先執行外層函數func,由於是閉包,因此外層函數變量a常駐內存
    fn()   # 直接執行內層函數inner,用到了閉包的好處,而且後面程序隨時能夠直接經過fn()執行內層函數,而不用執行外層函數

 

三,迭代器

       咱們以前一直在用可迭代對象進行迭代操做,那麼到底什麼是可迭代對象?首先咱們先回顧一下目前咱們所熟知的可迭代對象有哪些,有str,list,tuple,dict,set,那爲何咱們能夠稱它們爲可迭代對象呢?由於它們都遵循了可迭代協議,什麼是可迭代協議?首先咱們看下面一段錯誤代碼:

# 正確
    s = 'abc'
    for c in s:
        print(c)

    # 錯誤
    for i in 123:
        print(i)
    # 結果:
    # Traceback (most recent call last):
    #   File "E:/pythonDemo/1-basis/test10.py", line 107, in <module>
    #     for i in 123:
    # TypeError: 'int' object is not iterable

 注意看報錯信息中有這樣一句話:'int' object is not iterable,翻譯過來就是整數類型對象是不可迭代的,iterable表示可迭代的,表示可迭代協議,那麼如何進行驗證你的數據類型是否符合可迭代協議,咱們能夠經過dir函數來查看類中定義好的全部方法。具體示例以下: 

   s = "個人哈哈哈"
    print(dir(s))   # 能夠打印對象中的方法和函數
    print(dir(str))   # 也能夠打印類中聲明的方法和函數

 

在打印結果中,尋找__iter__,若是能找到,那麼這個類的對象就是一個可迭代對象。咱們發如今字符串中能夠找到__iter__,繼續看一下list,tuple,dict,set,具體以下

    print(dir(list))
    print(dir(tuple))
    print(dir(dict))
    print(dir(set))
    print(dir(open("test9.py")))   # 文件對象

咱們發現這幾個能夠進行for循環的東西都有__iter__函數,包括range也有,能夠本身試一下。

綜上可知,經過尋找__iter__能夠查看一個對象是不是可迭代對象,除此以外,咱們還能夠經過isinstence()函數來查看一個對象是什麼類型的,具體示例以下:

    ls = [1,2,3]
    ls_iter = ls.__iter__()   # 獲取列表ls的迭代器
    from collections import Iterable
    from collections import Iterator
    print(isinstance(ls,Iterable))   #True  ls是可迭代對象的一個實例
    print(isinstance(ls,Iterator))    #False  ls不是迭代器的一個實例
    print(isinstance(ls_iter,Iterable))   #True  ls_iter是可迭代對象的一個實例
    print(isinstance(ls_iter,Iterator))   #True  ls_iter是迭代器的一個實例

綜上,咱們能夠肯定,若是對象中有__iter__函數,那麼咱們認爲這個對象遵照了可迭代協議,就能夠獲取到相應的迭代器,上面代碼中的__iter__就是幫咱們獲取到對象的迭代器,咱們使用迭代器中的__next__()來獲取到一個迭代器中的元素,那麼咱們以前講的for循環的工做原理究竟是什麼呢?繼續看下面代碼:  

    s = "我愛北京天安門"
    c = s.__iter__()   # 獲取迭代器
    print(c.__next__())   # 使用迭代器進行迭代,獲取一個元素:我
    print(c.__next__())   # 愛
    print(c.__next__())   # 北
    print(c.__next__())   # 京
    print(c.__next__())   # 天
    print(c.__next__())   # 安
    print(c.__next__())   # 門
    print(c.__next__())   # StopIteration

for循環以下:  

for i in [1,2,3]:
    print(i)

 下面使用while循環+迭代器來模擬for循環(必需要掌握): 

    lst = [1,2,3]
    lst_iter = lst.__iter__()
    while 1:
        try:
            i = lst_iter.__next__()
            print(i)
        except StopIteration:
            break

  

 

總結:

       Iterable:可迭代對象,內部包含__iter__()函數,不包含__next__()函數;

       Iterator:迭代器,內部包含__iter__()函數,同時包含__next__()函數;

       迭代器特色:

              1,節省內存(下篇生成器中介紹);

              2,惰性機制(遇到__next__才取一個);

              3,不能反覆,只能向下執行;

       咱們能夠把要迭代的內容當成子彈,而後呢,獲取到迭代器__iter__(),就把子彈都裝在彈夾中,而後發射就是__next__()把每個子彈(元素)打出來,也就是說,for循環的時候,一開始是__iter__()來獲取迭代器,後面每次獲取元素都是經過__next__()來完成的,當程序遇到StopIteration將結束循環。

相關文章
相關標籤/搜索