Python裝飾器語法與應用#學習猿地


### 裝飾器定義

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

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

**應用場景**:引入日志,函數執行時間的統計,執行函數前的準備工作,執行函數后的處理工作,權限校驗,緩存等

**語法規則**:在原有的函數上加上 @符,裝飾器會把下面的函數當作參數傳遞到裝飾器中,@符又被成為 語法糖

#### 1.裝飾器原型(閉包)

```python

# 1。 裝飾器的原型

### 利用閉包,把函數當作參數傳遞,并且在函數內去調用傳遞進來的函數,并返回一個函數

# 定義外函數,接收一個函數作為參數

def outer(f):

? ? # 定義內函數,并且在內函數中調用了外函數的參數

? ? def inner():

? ? ? ? print('我是外函數中的內函數1')

? ? ? ? f()

? ? ? ? print('我是外函數中的內函數2')

? ? return inner

# 定義普通函數

# def old():

#? ? print('我是一個普通的函數')

#

# # old()? # 作為普通函數直接調用

# old = outer(old)? # outer返回了inner函數,賦值給了old

# old()? ? ? ? ? ? # 此時再調用old函數時,等同于調用了 inner 函數

# 改為裝飾器用法

@outer? ? ? # 此處使用的@outer的語法就是把outer作為了裝飾器,等同于 old = outer(old)

def old():

? ? print('我是一個普通的函數')

old()? # old函數經過 outer裝飾器進行了裝飾,代碼和調用方法不變,但是函數的功能發送了改變

```

#### 2.裝飾器的應用:統計函數的執行時間

```python

# 裝飾器應用場景-統計函數執行時間

import time

# 定義一個統計函數執行時間的 裝飾器

def runtime(f):

? ? def inner():

? ? ? ? start = time.perf_counter()

? ? ? ? f()

? ? ? ? end =? time.perf_counter() - start

? ? ? ? print(f'函數的調用執行時間為:{end}')

? ? return inner

# 定義一個函數

@runtime

def func():

? ? for i in range(5):

? ? ? ? print(i,end=" ")

? ? ? ? time.sleep(1)

func()

```

#### 3.裝飾器嵌套語法

```python

# 1.定義裝飾器

# 外函數

def outer(func):

? ? #內函數

? ? def inner():

? ? ? ? print('找到妹子,成功拿到微信。。。3')

? ? ? ? func()? # 在內函數中調用外函數中的行參-函數

? ? ? ? print('約妹子,看一場午夜電影。。。4')

? ? # 在外函數中返回內函數

? ? return inner

# 2。在定義一個裝飾器

def kuozhan(f):

? ? def kzinner():

? ? ? ? print('擴展1')

? ? ? ? f()

? ? ? ? print('擴展2')

? ? return kzinner

# 3. 裝飾器的嵌套 先執行下面的,再執行上面的。

@kuozhan # 2。再使用上面的 kuozhan 裝飾器,裝飾 上一次返回的 inner 函數,又返回了 kzinner 函數

@outer? # 1。先使用離得近的 outer裝飾器 裝飾love函數,返回了一個 inner函數

def love():

? ? print('跟妹子暢談人生和理想。。。5')

love()

''' 結果和過程的解析

1 3 5 4 2

1 先使用離得近的 outer裝飾器 裝飾love函數,返回了一個 inner函數

2 再使用上面的 kuozhan 裝飾器,裝飾 上一次返回的 inner 函數,又返回了 kzinner 函數

最后在調用love函數的時候是怎么執行的

? ? love() == kzinner()

? ? ? ? ? ? ? ? ===>? 1

? ? ? ? ? ? ? ? ===>? inner()?

? ? ? ? ? ? ? ? ? ? ? ? ? ===> 3

? ? ? ? ? ? ? ? ? ? ? ? ? ===> love() ===> 5

? ? ? ? ? ? ? ? ? ? ? ? ? ===> 4

? ? ? ? ? ? ? ? ===>? 2

'''

```

#### 4.對帶有參數的函數進行裝飾

```python

# 定義裝飾器

def outer(func):

? ? # 如果裝飾器帶有參數的函數,需要在內函數中定義行參,并傳遞給調用的函數。因為調用原函數等于調用內函數

? ? def inner(var):

? ? ? ? print(f'找到{var}妹子,成功拿到微信。。')

? ? ? ? func(var)

? ? ? ? print(f'約{var}妹子,看一場午夜電影。。')

? ? return inner

# 有參數的函數

@outer

def love(name):

? ? print(f'跟{name}妹子暢談人生。。。')

love('思思') #love() ==> inner()? love('思思') ===> inner('思思')

```

#### 5.對多參數的函數進行裝飾

```python

# 裝飾帶有多參數的函數

def outer(func):

? ? def inner(who,name,*args,**kwargs):

? ? ? ? print('約到妹子,聊微信。。。')

? ? ? ? func(who,name,*args,**kwargs)

? ? ? ? print('天色一晚,怎么辦?')

? ? return inner

# 定義多參數的 函數

@outer

def love(who,name,*args,**kwargs):

? ? print(f'{who}跟{name}暢談人生。。。')

? ? print('完事去吃了好多美食',args)

? ? print('看了一場電影',kwargs)

love('三多','思思','火鍋','辣條','7塊錢的麻辣燙',mov='唐山大地震')

'''

love() ==> inner()

? ? love(...) ==> inner(...)

? ? ? ? inner(...) ==> love(...)

'''

```

#### 6.帶有參數的裝飾器

> 你會遇到帶有參數的裝飾器,例如Django框架中的 @login_required(login_url='/accounts/login/')

```python

# 如果你的裝飾器需要有參數,那么給當前的裝飾器套一個殼,用于接收裝飾器的參數

def kuozhan(var):

? ? def outer(func):

? ? ? ? def inner1():

? ? ? ? ? ? print('妹子給了你微信')

? ? ? ? ? ? func()

? ? ? ? def inner2():

? ? ? ? ? ? print('妹子給介紹了個大媽')

? ? ? ? ? ? func()

? ? ? ? # 裝飾器殼的參數,可以用于在函數內去做流程控制

? ? ? ? if var == 1:

? ? ? ? ? ? return inner1

? ? ? ? else:

? ? ? ? ? ? return inner2

? ? return outer

@kuozhan(2) # kuozhan(var) ==> outer() ==> outer(love) ==> inner()

def love():

? ? print('談談人生。。。')

love()

```

#### 7.用類裝飾器裝飾函數

```python

# 類裝飾器裝飾函數

class Outer():

? ? # 魔術方法:當把該類的對象當作函數調用時,自動觸發 obj()

? ? def __call__(self,func):

? ? ? ? self.func = func? # 把傳進來的函數作為對象的成員方法

? ? ? ? return self.inner # 返回一個函數

? ? # 在定義的需要返回的新方法中 去進行裝飾和處理

? ? def inner(self,who):

? ? ? ? print('拿到妹子的微信。。。')

? ? ? ? self.func(who)

? ? ? ? print('看一場午夜電影。。。')

@Outer()? # Outer() ==> obj? @obj==>obj(love) ==> __call__(love) ==> inner()

def love(who):

? ? print(f'{who}和妹子談談人生和理想。。。')

love('川哥') # inner('川哥')

print(love) # 此時的 love就是屬于Outer類這個對象中的inner方法

```

#### 8.用類方法裝飾函數

```python

# 用類方法裝飾函數

class Outer():

? ? def newinner(func):

? ? ? ? Outer.func = func? # 把傳遞進來的函數定義為類方法

? ? ? ? return Outer.inner # 同時返回一個新的類方法

? ? def inner():

? ? ? ? print('拿到妹子微信')

? ? ? ? Outer.func()

? ? ? ? print('看一場午夜電影')

@Outer.newinner? # Outer.newinner(love) ==> Outer.inner

def love():

? ? print('和妹子談談人生喝喝茶。。。')

love()? ? ? ? ? # love()? ==> Outer.inner()

```

到目前為止以上所以形式的裝飾器,包括 函數裝飾器,類裝飾器,類方法裝飾器,都有一個共同特點:都是在給函數去進行裝飾,增加功能。

---

### 用裝飾器裝飾類

> 還有一種裝飾器,是專門裝飾類的。也就是在類的定義的前面使用@裝飾器這種語法

> @裝飾器

> class Demo():

>? ? pass

> 裝飾器給函數進行裝飾,目的是不改變函數調用和代碼的情況下給原函數增加了新的功能。

> 裝飾器給類進行裝飾,目的是不改變類的定義和調用的情況下給類增加新的成員(屬性或方法)。

#### 9.用函數裝飾器裝飾類

```python

# 使用函數裝飾器,給類進行裝飾,增加新的屬性和方法

# 定義函數,接收一個類。返回修改后的類

def kuozhan(cls):

? ? def func2():

? ? ? ? print('我是在裝飾器中追加的新方法,func2')

? ? cls.func2 = func2 # 把剛才定義的方法賦值給 類

? ? cls.name = '我是在裝飾器中追加的新屬性 name'

? ? #返回時,把追加類新成員的 類 返回去

? ? return cls

@kuozhan? # kuozhan(Demo) ==> cls ==> Demo

class Demo():

? ? def func():

? ? ? ? print('我是Demo類中定義的func方法')

Demo.func() # 此時在調用的Demo類是通過裝飾器,更新過的Demo類

Demo.func2()

print(Demo.name)

```

#### 10.使用類裝飾器裝飾類

```python

class KuoZhan():

? ? def __call__(self, cls):

? ? ? ? # 把接收的類,賦值給當前對象,作為一個屬性

? ? ? ? self.cls = cls

? ? ? ? # 返回一個函數

? ? ? ? return self.newfunc

? ? def newfunc(self):

? ? ? ? self.cls.name = '我是在類裝飾器中追加的新屬性 name'

? ? ? ? self.cls.func2 = self.func2

? ? ? ? # 返回傳遞進來的類的實例化結果,obj

? ? ? ? return self.cls()

? ? def func2(self):

? ? ? ? print('我是在類裝飾器中追加的新方法 func2')

@KuoZhan()? # KuoZhan() ==> obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc

class Demo():

? ? def func(self):

? ? ? ? print('我是Demo類中定義的func方法')

obj = Demo()? # Demo() ==> newfunc() ==> obj

obj.func()

obj.func2()

print(obj.name)

# 思考: 此時的 obj這個對象,是哪個類的對象。Demo還是KuoZhan

print(obj) # 此時的obj依然是Demo類的實例化對象,只不過經過裝飾后,增加了新的屬性和方法

```

作業:如何用一個裝飾器,裝飾帶有返回值的函數


掌握學習方法,不如會彎道超車!

學習猿地:成就自己的只需一套精品!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容