python基礎三_03_閉包_裝飾器_遞歸函數

python基礎三_03_閉包_裝飾器_遞歸

導讀:
本文主要記錄自己學習python3基礎中閉包、裝飾器和遞歸的要點知識和例子;僅供自己梳理。搞清楚函數的作用域是理解閉包,裝飾器的前提。

一、閉包

1.什麼是閉包?

(1).閉包的定義

·閉包函數必須有內嵌函數;
·閉包函數必須返回內嵌函數;
·內嵌函數可以引用該嵌套函數上一級namespace(命名空間)中的變量;

(2).閉包的好處

·使代碼變得簡潔;
·提高代碼的拓展性;

2.爲什麼要用閉包?

閉包能夠實現一個封閉的作用域

3.怎麼用閉包?

(1).定義一個閉包函數

對於初學者,就算理解不了閉包的實質,但是這種寫法是固定的,記下來就行。
# 定義
def outer():
    # 內嵌函數
    def inner():
        print('我是inner')
    # 返回內嵌函數。python裏變量都是指針,返回函數其實就是返回了一個內存地址
    return inner	# 是返回函數,不是調用函數,切忌寫成inner()

# 調用
new_Func_Name = outer()
new_Func_Name()
# 這兩步調用的是內嵌函數inner,類似於:outer()()
# print(func)
# <function outer.<locals>.inner at 0x00000209DE974048>
# 可以看到func就是inner,所以func的調用就是inner的調用。func() === inner() 

(2).理解閉包

閉包能夠實現一個封閉的作用域,內嵌函數inner可以使用外部函數的變量。說白了,閉包,閉包,閉的就是作用域,變量。
例一:內嵌函數使用外部函數的變量
def outera():
    num = 20
    def innera(num_in):
        print('我是inner,num_in is {}'.format(num_in))
        return num + num_in
    return innera

funa = outera()
# 調用
print(funa(40))
print(funa(50))
print(funa(1))

怎麼樣?有點明白什麼是閉包了嗎?

例:還可以在進行傳參
def outerb(num):
    def innerb(num_in):
        print('我是inner,num_in is {}'.format(num_in))
        return num + num_in
    return innerb

funb = outerb(20)
#
print(funb(2))
例:還可以這樣
def outerc(num):
    c = 100
    def innerc(num_in):
        print('我是inner,num_in is {}'.format(num_in))
        return num + num_in + c
    return innerc

func = outerc(20)
#
print(func(2))

二、裝飾器

如果理解了閉包,那麼裝飾器就很好理解了,前面說的閉包就是它的前身。

1.裝飾器的定義

裝飾器用來在原有的函數上增添新的代碼需求,裝飾器是程序開發中經常會用到的一個功能。

2.應用場景

當我們已經寫好一個函數時,項目也已經上線了,突然客戶想要增添一個需求,讓這個函數處理的更加細緻,這個就可以用到裝飾器了;

淘寶登錄驗證。在使用淘寶購物,

3.執行順序:

先返回內嵌函數–>有參數直接傳參給內嵌函數–>執行內嵌函數

4.結合典例理解裝飾器

場景:線上代碼如下,在不改變源碼的情況下,要求增添一個新功能,輸出:‘I come from china’。要求分別使用閉包和裝飾器來實現

def func1():
    print('this is ydxq')
def func2():
    print('this is bjl')
#
func1()
func2()
需求的輸出結果如下:

在這裏插入圖片描述

法一:閉包實現
# 源代碼
def func1():
    print('this is ydxq')
    
# 增加閉包函數
def outer(func):
    def inner():
        func()
        print('I come from china')
    return inner

# f1 = outer(func1)
# f1()
# 由於python是動態類型語言,那我們乾脆直接重新賦值一下func1,說白了就是把這個'f1'改成'func1'
func1 = outer(func1)
func1()    # 這樣是不是實現了咱們添加功能的需求?秀吧?
法二:裝飾器實現

裝飾器其實就是閉包更秀一點的寫法,叫 ‘@語法糖’,採用裝飾器的寫法,感覺代碼更加整潔。

# 先寫一個閉包
def outer1(func):
    def inner1():
        func()
        print('I come from china')
    return inner1

# 然後在原來的函數上面,寫'@+閉包函數名'
@outer1    # 這個意思就是:func1 = outer(func1)
def func1():
    print('this is bjl')
# 調用
func1()

可以return

def outer2(func):
    def inner2(name):
        print(name)
        data = func()
        print('I come from china')
        return data+name
    return inner2


@outer2
def func2():
    print('func4 this is bjl')
    return 'coll '
# 調用
print(func2('ydxq'))

當然,我們也可以傳參

def outer3(func):
    def inner3(name):
        # name的作用域只有inner3裏面這塊
        print(name)
        func()
        print('I come from china')
    return inner3

@outer3    # 這個意思就是:func1 = outer(func1)
def func3():
    print('this is bjl')

func3('ydxq')

可以這樣

def outer4(func):
    def inner4(name):
        # name的作用域只有inner3裏面這塊
        print(name)
        func(name)
        print('I come from china')
    return inner4

@outer4    # 這個意思就是:func1 = outer(func1)
def func4(name):
    print('func4 this is bjl, {}'.format(name))

func4('YDXQ')

甚至可以這樣

def outer5(func):
    def inner5(name,*args,**kwsrgs):
        data = func(*args,**kwsrgs)
        print('I come from china')
        return data + name
    return inner5

@outer5    # 這個意思就是:func1 = outer(func1)
def func5(name,*args,**kwsrgs):
    print('this is bjl')
    print(name,*args,**kwsrgs)
    return 'coll'

#func5('WSD')
print(func5('WSD',1,2,3,4,{'a':'1','b':'2','c':'3'}))

三、遞歸函數(不是重點)

1.遞歸函數簡介

(1).函數的內部可以調用其他函數

def func1():
    print('func1')
    
def func2():
    print('func2')

# main函數用於控制函數調用順序,約定俗稱的名字還有:run()、work()等
def main():
    func2()
    func1()

main()

在這裏插入圖片描述

(2).既然在函數當中能夠調用其他函數,那在函數中調用自己可以嗎?可以的

如果一個函數在內部調用自身,這個函數就是遞歸函數,遞歸會形成一個深度循環!

在這裏插入圖片描述

2、示例

例一:階乘

法一:for 循環,廣度循環
num = int(input('>>>'))
result = 1
for i in range(1,num+1):
    result *= i

print(result)

在這裏插入圖片描述

法二:while循環,深度循環
i = 1
result = 1
num = int(input('>>>'))

while i < num+1:
    result = result * i
    i += 1

print(result)

在這裏插入圖片描述

法三:遞歸函數:注意遞歸出口的設置
num = int(input('>>>'))
def test(n):
    # 遞歸出口,如果沒有遞歸出口就會造成棧溢出的情況
    if n == 1:
        return 1
    else:
        return n * test(n-1)

result = test(num)
print(result)

在這裏插入圖片描述

注意遞歸出口的設置
num = int(input('>>>'))
def test(n):
        return n * test(n-1)

result = test(num)
print(result)

在這裏插入圖片描述

例二:斐波那契數列

法一:普通while循環,動態類型語言,序列解包賦值
def feibo(n):
    a,b = 0,1
    c = []
    while n > 0:
        c.append(b)
        a,b = b,a+b
        n -= 1
    print(c)
    
feibo(5)

在這裏插入圖片描述

法二:遞歸函數
n = 5
def feibo(n):
    # 兩個條件控制遞歸出口
    if n <= 1:
        return n
    if n == 2:
        return 1
    return feibo(n - 1) + feibo( n - 2)

res = [feibo(i) for i in range(1,n+1)]
print(res)

在這裏插入圖片描述

解決棧溢出-----尾遞歸

return 後面沒有表達式 就是尾遞歸。但是python目前解決不了,所以不建議使用,知道就行。

四、總結與參考鏈接

1.總結

(1).想要真正理解閉包裝飾器,首先要理解函數的作用域。因爲閉包就是一個封閉包裹了它所能使用的作用域的函數。

(2).這部分的內容對初學者來說確實不好理解,比如我(小聲比比)。所以建議大家多從這三個方向思考:是什麼?爲什麼?怎麼用?希望能幫助你理清思路。另外本人也是新手上路,如果有不足和錯誤的地方希望您能批評指正。

2.參考鏈接:

作用域和閉包

python中閉包詳解

爲什麼要使用閉包和如何使用閉包

什麼是閉包?爲什麼使用閉包?閉包的缺點?

遞歸參考鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章