Python進階之閉包和裝飾器

Stefan的程式碼小屋發表於2018-04-23

閉包:

閉包 :一個函式裡面巢狀一個函式,呼叫外層函式返回裡層函式本身

示例1:

def fx(x):
    x +=1
    def fy(y):
        return  x*y
    return fy     # 不要加括號
f = fx(5)   # fy
n = f(5)   #fy()
print(n)  #30
複製程式碼

示例2(入參是一個函式):

def f1(f2):
    print("f1執行")
    def f3(b):
        print("f3執行",f2(b)+1)
    return f3

def f2(a):
    return a+a
f=f1(f2)# f3
f(4)# f3()
#可以直接f=f1(f2)(4)
執行結果:
f1執行
f3執行 9
複製程式碼

裝飾器:

裝飾函式的引數是被裝飾的函式物件,返回原函式物件裝飾器本質上是一個Python函式,它可以讓其他函式在不需要做任何程式碼變動的前提下增加額外功能,裝飾器的返回值也是一個函式物件 概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。

示例1(還是剛才的f1,f2):

def f1(f2):
    print("f1執行")
    def f3(b):
        print("f3執行",f2(b)+1)
    return f3
    
@f1
def f2(a):
    return a+a    
f2(4)#9
#本來f2是計算一個數的平方,現在加了一個@f1修飾就成一個數的平方+1了。
複製程式碼

在f2上加一個@f1,f1就是裝飾器,而剛才演示的閉包的實現原理就是裝飾器實現的原理。也可以說裝飾器也是閉包。

還有一個更好詮釋裝飾器的例子:

#計算一個函式執行的時間:
import time
def countTime(fun):
    def count(*args,**kwargs):
        t1=time.time()
        print("開始時間:",t1)
        back=fun(*args,**kwargs)
        t2=time.time()
        print("結束時間:",t2)
        print("總用時:",t2-t1)
        return back
    return count

@countTime
def f(a):
    for i in range(a):
        print(i)

f(1000)
#輸出結果:
'''
開始時間: 1517415914.1400516
1
2
...
999
結束時間: 1517415914.144096
總用時: 0.004044294357299805
'''
複製程式碼

類裝飾器

把一個類裝飾到一個函式上。

示例:

class Tset_Class():
    def __init__(self,func):
        print('正在例項化')
        self.func = func
    def __call__(self, *args, **kwargs):   # 一定要寫這個call
        print('這是call方法')
        return self.func

@Tset_Class
def  test():
    print('這是一個測試函式')

n =  test()   #    self.func : test 函式體
n() #這是一個測試函式
複製程式碼

類裝飾器的執行過程:

#  1.  @Tset_Class  :  Tset_Class( test )  -> 相當於:t = Tset_Class( test )
#  2.  test()  ->  相當於:  t()  呼叫例項的call方法 -> 返回 self.func函式體
#  3.  n =  test()   ,n()  呼叫test()
複製程式碼

通過2.知道必須要寫call方法

三個常用的裝飾器

@property 裝飾過的函式返回的不再是一個函式,而是一個property物件 裝飾過後的方法不再是可呼叫的物件,可以看做資料屬性直接訪問。

示例:

class Test():
    def __init__(self,name):
        self.__name = name

    @property  # 把方法變成屬性
    def get_name(self):
        return  self.__name

    @get_name.setter   # 可以讓get方法,變成set方法
    def get_name(self,name):
        self.__name = name

    @property  # 把方法變成屬性
    def po(self):
        print('asdfasdfasdfasdaf')
        
t = Test('xfy')        
print(t.get_name)#xfy
t.get_name = "stefan" #stefan
print(t.get_name)
t.po#123
複製程式碼

Test類裡有一個私有屬性__name,這樣在外界是不能訪問的,曾經我們通過寫一個get_name方法來獲得__name的值,但是每次呼叫都是t.get_name(),現在加了@property,讓這個方法變成像一個屬性一樣訪問。

再加一個這樣的@get_name.setter,就具備了賦值的能力。

@staticmethod #(靜態方法)

@classmethod (類方法)

這兩個裝飾器詳情請見Python學習之物件導向(封裝、繼承、多型)

對於裝飾器的學習本篇只是初步,它的用途很大,主要作用就是許可權控制,插入日誌,效能測試,事務處理,快取等。 還得等實際專案中碰到了,再深入學習。

有任何疑問,請留言評論。

相關文章