Python裝飾器進階

裝飾器進階

如今,咱們已經明白了裝飾器的原理。接下來,咱們還有不少事情須要搞清楚。好比:裝飾帶參數的函數、多個裝飾器同時裝飾一個函數、帶參數的裝飾器和類裝飾器。python

裝飾帶參數函數

def foo(func):  # 接收的參數是一個函數名
    def bar(x, y):  # 這裏須要定義和被裝飾函數相同的參數
        print("這裏是新功能...")  # 新功能
        func(x, y)  # 被裝飾函數名和參數都有了,就能執行被裝飾函數了
    return bar


# 定義一個須要兩個參數的函數
@foo
def f1(x, y):
    print("{}+{}={}".format(x, y, x+y))


# 調用被裝飾函數
f1(100, 200)

輸出:閉包

這裏是新功能...
100+200=300

多個裝飾器

def foo1(func):
    print("d1")

    def inner1():
        print("inner1")
        return "<i>{}</i>".format(func())

    return inner1


def foo2(func):
    print("d2")

    def inner2():
        print("inner2")
        return "<b>{}</b>".format(func())

    return inner2


@foo1
@foo2
def f1():
    return "Hello Andy"

# f1 = foo2(f1) ==> print("d2") ==> f1 = inner2
# f1 = foo1(f1) ==> print("d1") ==> f1 = foo1(inner2) ==> inner1

ret = f1()  # 調用f1() ==> inner1() ==> <i>inner2()</i> ==> <i><b>inner1()</b></i> ==> <i><b>Hello Andy</b></i>
print(ret)

帶參數裝飾器

被裝飾的函數能夠帶參數,裝飾器一樣也能夠帶參數。函數

回頭看咱們上面寫得那些裝飾器,它們默認把被裝飾的函數當成惟一的參數。可是呢,有時候咱們須要爲咱們的裝飾器傳遞參數,這種狀況下應該怎麼辦呢?post

接下來,咱們就一步步實現帶參數的裝飾器:spa

首先咱們來回顧下上面的代碼:code

def f1(func):  # f1是咱們定義的裝飾器函數,func是被裝飾的函數
    def f2(*arg, **kwargs):  # *args和**kwargs是被裝飾函數的參數
        func(*arg, **kwargs)
    return f2

從上面的代碼,咱們發現了什麼?orm

個人裝飾器若是有參數的話,沒地方寫了…怎麼辦呢?string

仍是要使用閉包函數!it

咱們須要知道,函數除了能夠嵌套兩層,還能嵌套更多層:io

# 三層嵌套的函數1
def f1():    
    def f2():
        name = "Andy"       
        def f3():
            print(name)
        return f3    
    return f2

嵌套三層以後的函數調用:

f = f1()  # f --> f2
ff = f()  # ff --> f3
ff()  # ff() --> f3() --> print(name) --> Andy

注意:在內部函數f3中可以訪問到它外層函數f2中定義的變量,固然也能夠訪問到它最外層函數f1中定義的變量。

# 三層嵌套的函數2
def f1():
    name = "Andy"
    def f2():
        def f3():
            print(name)
        return f3
    return f2

調用:

f = f1()  # f --> f2
ff = f()  # ff --> f3
ff()  # ff() --> f3() --> print(name) --> Andy

好了,如今咱們就能夠實現咱們的帶參數的裝飾器函數了:

# 帶參數的裝飾器須要定義一個三層的嵌套函數
def d(name):  # d是新添加的最外層函數,爲咱們原來的裝飾器傳遞參數,name就是咱們要傳遞的函數
    def f1(func):  # f1是咱們原來的裝飾器函數,func是被裝飾的函數
        def f2(*arg, **kwargs):  # f2是內部函數,*args和**kwargs是被裝飾函數的參數
            print(name)  # 使用裝飾器函數的參數
            func(*arg, **kwargs)  # 調用被裝飾的函數
        return f2
    return f1

上面就是一個帶參裝飾器的代碼示例,如今咱們來寫一個完整的應用:

def d(a=None):  # 定義一個外層函數,給裝飾器傳參數--role
    def foo(func):  # foo是咱們原來的裝飾器函數,func是被裝飾的函數
        def bar(*args, **kwargs):  # args和kwargs是被裝飾器函數的參數
            # 根據裝飾器的參數作一些邏輯判斷
            if a:
                print("歡迎來到{}頁面。".format(a))
            else:
                print("歡迎來到首頁。")
            # 調用被裝飾的函數,接收參數args和kwargs
            func(*args, **kwargs)
        return bar
    return foo


@d() # 不給裝飾器傳參數,使用默認的'None'參數
def index(name):
    print("Hello {}.".format(name))


@d("電影") # 給裝飾器傳一個'電影'參數
def movie(name):
    print("Hello {}.".format(name))

if __name__ == '__main__':
    index("Andy")
    movie("Andy")

輸出:

歡迎來到首頁。
Hello Andy.
歡迎來到電影頁面。
Hello Andy.

類裝飾器和裝飾類

類裝飾器

除了用函數去裝飾函數外,咱們還可使用類去裝飾函數。

class D(object):
    def __init__(self, a=None):
        self.a = a
        self.mode = "裝飾"

    def __call__(self, *args, **kwargs):
        if self.mode == "裝飾":
            self.func = args[0]  # 默認第一個參數是被裝飾的函數
            self.mode = "調用"
            return self
        # 當self.mode == "調用"時,執行下面的代碼(也就是調用使用類裝飾的函數時執行)
        if self.a:
            print("歡迎來到{}頁面。".format(self.a))
        else:
            print("歡迎來到首頁。")
        self.func(*args, **kwargs)


@D()
def index(name):
    print("Hello {}.".format(name))


@D("電影")
def movie(name):
    print("Hello {}.".format(name))

if __name__ == '__main__':
    index("Andy")
    movie("Andy")

裝飾類

咱們上面全部的例子都是裝飾一個函數,返回一個可執行函數。Python中的裝飾器除了能裝飾函數外,還能裝飾類。

可使用裝飾器,來批量修改被裝飾類的某些方法:

# 定義一個類裝飾器
class D(object):
    def __call__(self, cls):
        class Inner(cls):
            # 重寫被裝飾類的f方法
            def f(self):
                print("Hello Andy.")
        return Inner


@D()
class C(object):  # 被裝飾的類
    # 有一個實例方法
    def f(self):
        print("Hello world.")


if __name__ == '__main__':
    c = C()
    c.f()
相關文章
相關標籤/搜索