1. 程式人生 > 實用技巧 >python中的裝飾器

python中的裝飾器

python語言中的裝飾器應該算是這門語言中的一個難點了,作為一個初學者,好像也知道是怎麼回事,可是否真正理解了,那還得存疑。最近偶然看到老男孩的Egon老師的視訊,我才覺得真正明白裝飾器是怎麼一回事了。Egon老師那種抽絲剝繭式的講解,真是讓人耳目一新。雖然有些時候略顯繁瑣,但對於一個困難知識點來說,再沒有這種方式更好的講解了。接下來,我就著剛看過視訊的熱乎近,把我對裝飾器的理解記錄下來,以防止哪一天忘記了。

  • 什麼是裝飾器
    - 裝飾器一種特殊物件,可以給其它物件新增功能,而不改變所裝飾物件的使用方式。比如說:如果一個函式被一個裝飾器裝飾,那麼從使用者的角度來看,應該感覺不到有任何變化,函式的呼叫方式,引數以及返回值都不發生任何變化。
    - 裝飾器用到了一個Python中的一個重要概念:閉包。什麼是閉包?條件有兩個:一是一定要有一個內部函式。二是這個內部函式一定要訪問包含這個內部函式的外層函式的變數。
    - 還利用了Python中的函式的特性:可以作為引數傳遞,也可以作為返回值被返回。
  • 如何來寫一個裝飾器(以函式裝飾器為例)
    - 我們先來考慮,如何給一個函式增加功能,卻不改變它的呼叫方式。
# sol3 - 1: 通過在內外層函式中使用兩次(*args, **kwargs),可以把外層函式的引數原封不動地傳遞到內層。
# 從而就可以把內層函式的引數寫活,但是變更了呼叫方式,要通過wrapper來呼叫index

def wrapper(*args, **kwargs):
    start = time.time()
    index(*args, **kwargs)
    stop = time.time()
    print(stop - start)


wrapper('xiaolee', 'zd')

# sol 3 -2: 要想不改變函式的呼叫方式,只能通過在wrapper函式外再包上一層函式。這個方案還不能接收內層函式的返回值。
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
    return wrapper


index = timer(index)
index('ggg', 'dddd')


# sol3 - 3:通過res變數接收內層函式的返回值,並作為wrapper函式的返回值返回。
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res
    return wrapper


index = timer(index)

print(index('zz', 'yy'))


# sol 4: 語法糖

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res
    return wrapper


@timer
def index(x, y):
    time.sleep(1)
    print('heloo %s, your company %s' % (x, y))


index('xxxx', 'ccccc')
      
  • 帶引數的裝飾器
# 帶參裝飾器

# 以加簡單的認證功能為例

def authentication():
    user = input('Please input your username: ').strip()
    password = input('Please input your password: ').strip()
    if user == 'zylee' and password == 'wlsoft':
        print('authentication successful!!!')
        return True
    else:
        print('authentication failed!!!')
        return False


def auth(db_style):
    def deco(func):
        def wrapper(*args, **kwargs):
            # auth code
            if db_style == 'mysql':
                if authentication():
                    res = func(*args, **kwargs)
                    return res
            elif db_style == 'file' and authentication():
                if authentication():
                    res = func(*args, **kwargs)
                    return res
            else:
                print("error!!!!")
        return wrapper
    return deco


@auth('mysql')
def index(x, y):
    print('hello, %s and %s' % (x, y))


index('a', 'b')