閉包函數、裝飾器、迭代器


超強台風利奇馬就這樣和上海擦肩而過了,今天的天氣依舊艷陽高照,不幸的是我的扁桃體發炎了,又腫又癢,如萬千螞蟻在嗓子里爬動,買盒金嗓子,堅持學習。今天學了閉包函數、裝飾器、迭代器,下面總結一下今天的知識點。

一、閉包函數

1、什么是閉包?

  • 閉包:閉是封閉(函數內部函數),包是包含(該內部函數對外部作用域而非全局作用域的變量的引用。

    閉包指的是:函數內部函數對外部作用域而非全局作用域的引用。

  • 閉包函數:傳參的另外一種方式,把函數+參數包在一起返回出去

def outter():
    x=1
    def inner():
        print(x)
    return inner

f=outter()  # f接收outter的返回值inner,其中包含inner+x

def f2():
    x=2
    f()  # =inner(),先在inner中找x的值,沒有就到outter中找,找到x=1,輸出
f2() 
---------------------------------------------------
最后輸出結果為1

1.1 兩種為函數傳參的方式

方式一:使用參數的形式

def func(x):
    print(x)

func(1)   # 把實參1傳給形參x
func(1)   # 把實參1傳給形參x
-----------------------------------------------------------
1
1

這種方式比較麻煩,每次傳參都得在func()中給定參數

方式二:包給函數

# 函數固定格式
def outter(x):
    x=1
    def inner():
        print(x)
    return inner

f=outter(1)   #得到返回值inner,其中包着inner+x=1
# 如果f=outter(2),輸出還是1,因為查找順序從print(x)當前位置開始,再到局部outter中x=1,不會再去找到傳參的參數
f()   # =inner(),其中x=1
f()
f()
-----------------------------------------------------------
1
1
1


2、閉包函數的應用

  • 閉包的意義:返回的函數對象,不僅僅是一個函數對象,在該函數外還包裹了一層作用域,這使得該函數無論在何處調用,優先使用自己外層包裹的作用域。
  • 應用領域:延遲計算(原來我們是傳參,現在是把函數和變量一起包起來)、爬蟲領域。

例如要定義一個爬起網站的閉包函數:

def outter(url):
    def get_res():
        res=requests.get(url)
        print(res.text)
    return get_res

baidu_spider=outter('https://www.baidu.com')  # baidu_spider=ger_res+url='https://www.baidu.com',直接就把'https://www.baidu.com'傳給了url
baidu_spider()  # =get_res(),其中url='https://www.baidu.com'

#  這種想改網址,可以直接在調用階段改實參
taobao_spider=outter('https://www.taobao.com')
taobao_spider()

二、裝飾器

1、什么是裝飾器

器指的是工具,而程序中的函數就是具備某一功能的工具,所以裝飾器指的是為被裝飾函數添加額外功能。裝飾器本質就是一個函數A,被裝飾的對象也是一個函數B,用函數A去裝飾函數B。

注意:

  • 裝飾器本身其實是可以任意可調用的對象
  • 被裝飾的對象也可以是任意可調用的對象

2、裝飾器的實現的兩大原則

1、不修改被裝飾對象的源代碼

2、不修改被裝飾對象的調用方式

3、裝飾器模板

應用舉例:打印程序運行時間

def deco(func):
    """裝飾器函數"""
    def f1():
        start=time.time()
        func()         
        end=time.time()
        print(end-start)
    return f1

import time
def index():
    """被裝飾函數"""
    print('hello index')
    time.sleep(1)
    
index=deco(index)  # 把index傳給deco的形參func,這里的前一個index=f1(),其中func=index(被裝飾的函數名index)
index()

裝飾器模板:

def deco(func):
    def wrapper(*args,**kwargs):
        # 這里加要添加的功能
        res=func(*args,**kwargs)
        return res
    return wrapper
@deco
def shopping():
    print('shopping')

4、裝飾器語法糖

在被裝飾函數正上方,並且是單獨一行寫上@裝飾器名

def deco(func):
    """裝飾器函數"""
    def f1():
        start=time.time()
        func()         
        end=time.time()
        print(end-start)
    return f1

@deco  # 語法糖(更精簡的代碼),代替index=deco(index),可以用@deco()給deco傳參
import time
def index():
    """被裝飾函數"""
    print('hello index')
    time.sleep(1)
    
# index=deco(index) 
index()

5、三層裝飾器

5.1 閉包包三層的運用

def f1(y):

    def f2():
        x = 1

        def f3():
            print(f"x: {x}")
            print(f"y: {y}")
        return f3
    return f2

f2 = f1(2)
f3 = f2()
f3()
x: 1
y: 2

5.2 三層裝飾器:給雙層裝飾器加參數

5.3 三層裝飾器模板

def sanceng(engine):
    def outter(func):
        def wrapper(*args, **kwargs):  # wrapper是未來要運行的函數
            # 加功能
            print(engine)
            res = func(*args, **kwargs)  # func是被裝飾的函數
            return res
        return wrapper
    return outter

@sanceng('file')
def shopping():
    print('shopping')

例如:判斷賬號密碼來自於哪個地方

def auth(engine):
    def login(func):
        def inner(*args,**kwargs):
            # 給購物函數添加登錄功能
            if engine=='file':
                username=input('username:')
                pwd=input('pwd:')
                if username=='zyl'and pwd=='123':
                    print('登錄成功')
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('登錄失敗')
            elif engine=='db':
                print('賬號密碼來自於數據庫,非法請求')
         return inner
    return login

@ auth('file')
def shopping():
    # 被裝飾函數
    print('shopping')
    
shopping()
                   

三、迭代器

迭代器:迭代的工具。更新換代、重復,基於上一次的結果推出下一次的結果

3.1 可迭代對象

定義:具有__iter__方法的對象就是可迭代對象,除了數字類型和函數類型

可迭代對象無論迭代多少次,他的內存地址是不變的

注意:tuple(1)與tuple(1,)類型有區別

# 可迭代(具有__iter__方法)   對象

x = 1  # 不可迭代對象

s = 'nick'  # 可迭代對象
s.__iter__()
lt = [1, 2, 3]  # 可迭代對象
dic = {'a': 1, 'b': 2}  # 可迭代對象
tup = (1,)  # 元組只有一個元素必須得加逗號# 可迭代對象
se = {1, 2, 3}  # 可迭代對象
f = open('time.py')  # 可迭代對象

def func():  # 不可迭代對象
    pass

# 有__iter__()方法的對象就是可迭代對象,然后除了數字類型和函數之外都是可迭代對象
 s = 'nick'
 s_iter = s.__iter__()
 print(s_iter.__next__())  # 基於索引(基於上一次結果)通過__next__進行迭代
 print(s_iter.__next__())
 print(s_iter.__next__())
    

3.2 迭代器對象

只有字符串和列表都是依賴索引取值的,而其他的可迭代對象都是無法依賴索引取值的。因此我們得找到一個方法能讓其他的可迭代對象不依賴索引取值。

在找到該方法前,首先我們給出迭代器對象的概念:可迭代的對象執行__iter__方法得到的返回值。並且可迭代對象會有一個__next__方法。

# 不依賴索引的數據類型迭代取值
dic = {'a': 1, 'b': 2, 'c': 3}
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
# print(iter_dic.__next__())  # StopIteration:
----------------------------------------------------------------
a
b
c
# 依賴索引的數據類型迭代取值
lis = [1, 2, 3]
iter_lis = lis.__iter__()
print(iter_lis.__next__())
print(iter_lis.__next__())
print(iter_lis.__next__())
# print(iter_lis.__next__())  # StopIteration:
----------------------------------------------------------------
1
2
3

上述的方法是非常繁瑣的,我們可以使用while循環精簡下。其中使用的try...except...為異常處理模塊

s = 'hello'
iter_s = s.__iter__()

while True:
    try:
        print(iter_s.__next__())
    except StopIteration:
        break
----------------------------------------------------------------
h
e
l
l
o
# 迭代器對象: 具有__iter__以及__next__方法的叫做迭代器對象
s = 'nick'  # 可迭代對象,不屬於迭代器對象
s.__iter__()
lt = [1, 2, 3]  # 可迭代對象,不屬於迭代器對象
dic = {'a': 1, 'b': 2}  # 可迭代對象,不屬於迭代器對象
tup = (1,)  # 元組只有一個元素必須得加逗號# 可迭代對象,不屬於迭代器對象
se = {1, 2, 3}  # 可迭代對象,不屬於迭代器對象
f = open('time.py')  # 可迭代對象,迭代器對象

# 只有文件是迭代器對象

總結:

迭代器對象:執行可迭代對象的__iter__方法,拿到的返回值就是迭代器對象。

特點:

  1. 內置__next__方法,執行該方法會拿到迭代器對象中的一個值
  2. 內置有__iter__方法,執行該方法會拿到迭代器本身
  3. 文件本身就是迭代器對象。

缺點:

  1. 取值麻煩,只能一個一個取,並且只能往后取,值取了就沒了
  2. 無法使用len()方法獲取長度

可迭代對象: 具有__iter__方法的對象就是可迭代對象,除了數字類型和函數都是可迭代對象

迭代器對象: 具有__iter____next__方法的都是迭代器對象,只有文件

迭代器對象一定是可迭代對象; 可迭代對象不一定是迭代器對象

四、for循環原理

for循環稱為迭代循環,in后必須是可迭代的對象。

lt = [1,2,3]
lt_iter = lt.__iter__()  # 將它變為迭代器對象
while 1:
    try:
        print(lt_iter.__next__())
    except StopIteration:    # 對他進行異常捕捉
        break
使用for循環

for i in lt:   # 可迭代對象;迭代器對象  # 不依賴索引取值,而是迭代取值
    print(i)
 '''
 1. 把lt(可迭代對象/迭代器對象)用__iter__方法轉換成迭代器對象
 2. 使用__next__取出迭代器里的所有值
 3. 使用__next__方法取盡迭代器中的所有值,一定會報錯,通過異常捕捉退出while循環
 '''


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM