由淺入深,走進Python裝飾器-----第一篇:基礎

本文儘量用簡單代碼附以實例驗證, 逐步理解, 裝飾器原型

**裝飾器背景:**   在不改變原函數代碼,  且保持原函數調用方法不變的情況下,  給原函數增加新的功能 (或者給類增加新屬性和方法)


**核心思想: **      用一個函數(或者類) 去裝修 一箇舊函數 (或者類)   , 造出一個新函數(或者新類)

Python萬物皆對象, 可以隨意賦值使用

def func():
    print("第三: ","我是一個函數的輸出結果")
a = 1
print("第一: 我是一個變量 ",a)
a = func                                                        #將函數名賦值給變量
print("第二: 我是一個函數名 ",a)
a()
>>>第一: 我是一個變量 1
>>>第二: 我是一個函數名 <function func at 0x000000000220AAE8>
>>>第三: 我是一個函數的輸出結果

變量作用域

# 根據 LEGB原則, 內函數使用外函數的局部變量
def outer():
    a = 0
    print("我是外部函數的 a:",a)
    def inner():
        print("我是內部函數打印的 a:",a)

> > > 我是外部函數的 a: 0
> > > 我是內部函數打印的 a: 0

# 引入 nonlocal, 內函數可以修改外函數
def outer():
    a = 0
    print("我是外部函數的 a:",a)
    def inner():
        nonlocal a
        a += 1
        print("我是內部函數加工過的 a:",a)

>>>我是外部函數的 a: 0
>>>我是內部函數加工過的 a: 1

閉包( 延長局部變量生存時間 )

# 內函數  夾帶  外函數的局部變量
def outer():
    a = 0
    print("我是外部函數打印的 a:",a)
    def inner():
        print("我是內部函數打印的 a:",a)
    return inner

res = outer()                                        # res接收的是一個函數名
print("我是返回出來的內函數名: "res)
res()

>>>我是外部函數打印的 a: 0
>>>我是返回出來的內函數名: <function func at 0x000000000220AAE8>
>>>我是內部函數打印的 a: 0
>>>
#  內函數  夾帶  修改後的外函數的局部變量
def outer():
    a = 0
    print("我是外部函數打印的 a:",a)
    def inner():
        nonlocal a
        a += 1
        print("我是內部函數修改過的 a:",a)
    return inner

res = outer()                                       # res接收的是一個函數名
print("我是返回出來的內函數名: "res)
res()

>>>我是外部函數打印的 a: 0
>>>我是返回出來的內函數名: <function func at 0x000000000220CA97>
>>>我是內部函數修改過的 a: 1

函數名可以當做參數傳遞

#新函數對舊函數修飾
def old():
    print('我是 舊函數')     
def new(f):
    print('我是 新函數, 我可以輸出 舊函數')
    f() 

new( old )                        #把舊函數名當做參數, 傳遞給新函數, 在新函數內產生變化

>>>我是 新函數, 我可以輸出 舊函數
>>>我是 舊函數

裝飾器原型

# ### 利用閉包,將函數名當參數傳遞
def outer(f):
    def inner():
        print("我是外部傳進來的old :",f)
        f()
        print("我是修飾工,我在舊函數後顯示")
    return inner

def old():
    print("我是舊函數")

print("我是最初的old: ",old)                         
print("我是返回出來的內函數inner: ",outer(old))   #outer把函數名old當參數傳回函數內部進行加工, 把函數名inner返回
old = outer(old)                            #實際上, 舊函數名old已被重新賦值爲inner
print("我不是原來的old了 ",old)
old()                                       #經過改造, 雖然調用結果不變, 但是運行結果已變化

>>>我是最初的old:               <function old at 0x00000000021CAA60>
>>>我是返回出來內函數inner:     <functionouter<locals>.inner at 0x00000000021CAAE8>
>>>我不是原來的old了            <function outer.<locals>.inner at 0x00000000021CAAE8>
>>>我是外部傳進來的old :        <function old at 0x00000000021CAA60>
>>>我是舊函數
>>>我是修飾工,我在舊函數後顯示

裝飾器

# ### 用 @outer 修飾
def outer(f):
    def inner():
        print("我是外部傳進來的old :",f)
        f()
        print("我是修飾工,我在舊函數後顯示")
    return inner

@outer                       #實際上,將  old = outer(old)  這句改成 @outer, 放在old函數上方
def old():
    print("我是舊函數")

old()                                                                 
>>>我是外部傳進來的old : <function old at 0x00000000021CAA60>
>>>我是舊函數
>>>我是修飾工,我在舊函數後顯示

裝飾器的嵌套: 下層裝飾器修飾後的函數, 再次被上層裝飾器修飾

def kuozhan(f):
    def inner():
        print("1我是擴展函數,接收外部傳進來的新old :",f)
        f()
        print("4我是擴展函數,我在新old函數後顯示")
    return inner

def outer(f):
    def inner():
        print("2我是outer函數,接收外部傳進來的old :",f)
        f()
        print("3我是outer函數,我在old函數後顯示")
    return inner

@kuozhan               #old = kuozhan(outer(old))
@outer                 #old = outer(old)
def old():
    print("我是old函數")

old()

>>>1 我是擴展函數,接收外部傳進來的新old : <function outer.<locals>.inner at 0x000000000250AB70>
>>>2 我是外部傳進來的old :               <function old at 0x000000000250AAE8>
>>>  我是old函數
>>>3 我是修飾工,我在舊函數後顯示
>>>4 我是擴展函數,我在新old函數後顯示
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章