程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

如何通俗易懂地理解Python裝飾器?怎麼使用?

編輯:Python

目錄

一、裝飾器

1. 裝飾器的定義

2. 裝飾器的示例代碼

3. 裝飾器的語法糖寫法

二、裝飾器的使用

1. 裝飾器的使用場景

2. 裝飾器實現已有函數執行時間的統計

 三、通用裝飾器的使用

1. 裝飾帶有參數的函數

2. 裝飾帶有返回值的函數

3. 裝飾帶有不定長參數的函數

4. 通用裝飾器

四、多個裝飾器的使用

1. 多個裝飾器的使用示例代碼

五、帶有參數的裝飾器

1. 帶有參數的裝飾器介紹

 六、類裝飾器

1. 類裝飾器的介紹


一、裝飾器

1. 裝飾器的定義

就是給已有函數增加額外功能的函數,它本質上就是一個閉包函數

裝飾器的功能特點:

  1. 不修改已有函數的源代碼
  2. 不修改已有函數的調用方式
  3. 給已有函數增加額外的功能

2. 裝飾器的示例代碼

# 添加一個登錄驗證的功能
def check(fn):
def inner():
print("請先登錄....")
fn()
return inner
def comment():
print("發表評論")
# 使用裝飾器來裝飾函數
comment = check(comment)
comment()
# 裝飾器的基本雛形
# def decorator(fn): # fn:目標函數.
# def inner():
# '''執行函數之前'''
# fn() # 執行被裝飾的函數
# '''執行函數之後'''
# return inner

代碼說明:

  • 閉包函數有且只有一個參數,必須是函數類型,這樣定義的函數才是裝飾器。
  • 寫代碼要遵循開放封閉原則,它規定已經實現的功能代碼不允許被修改,但可以被擴展。

執行結果:

請先登錄....
發表評論

3. 裝飾器的語法糖寫法

如果有多個函數都需要添加登錄驗證的功能,每次都需要編寫func = check(func)這樣代碼對已有函數進行裝飾,這種做法還是比較麻煩。

Python給提供了一個裝飾函數更加簡單的寫法,那就是語法糖,語法糖的書寫格式是: @裝飾器名字,通過語法糖的方式也可以完成對已有函數的裝飾

# 添加一個登錄驗證的功能
def check(fn):
print("裝飾器函數執行了")
def inner():
print("請先登錄....")
fn()
return inner
# 使用語法糖方式來裝飾函數
@check
def comment():
print("發表評論")
comment()

說明:

  • @check 等價於 comment = check(comment)
  • 裝飾器的執行時間是加載模塊時立即執行。

執行結果:

請先登錄....
發表評論

二、裝飾器的使用

1. 裝飾器的使用場景

  1. 函數執行時間的統計
  2. 輸出日志信息

2. 裝飾器實現已有函數執行時間的統計

import time
# 裝飾器函數
def get_time(func):
def inner():
begin = time.time()
func()
end = time.time()
print("函數執行花費%f" % (end-begin))
return inner
@get_time
def func1():
for i in range(100000):
print(i)
func1()

執行結果:


...
99995
99996
99997
99998
99999
函數執行花費0.329066

 三、通用裝飾器的使用

1. 裝飾帶有參數的函數

# 添加輸出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力計算--")
fn(num1, num2)
return inner
# 使用裝飾器裝飾函數
@logging
def sum_num(a, b):
result = a + b
print(result)
sum_num(1, 2)

運行結果:

--正在努力計算--
3

2. 裝飾帶有返回值的函數

# 添加輸出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力計算--")
result = fn(num1, num2)
return result
return inner
# 使用裝飾器裝飾函數
@logging
def sum_num(a, b):
result = a + b
return result
result = sum_num(1, 2)
print(result)

運行結果:

--正在努力計算--
3

3. 裝飾帶有不定長參數的函數

# 添加輸出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力計算--")
fn(*args, **kwargs)
return inner
# 使用語法糖裝飾函數
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
print(result)
sum_num(1, 2, a=10)

運行結果:

--正在努力計算--
13

4. 通用裝飾器

# 添加輸出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力計算--")
result = fn(*args, **kwargs)
return result
return inner
# 使用語法糖裝飾函數
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
return result
@logging
def subtraction(a, b):
result = a - b
print(result)
result = sum_num(1, 2, a=10)
print(result)
subtraction(4, 2)

運行結果:

--正在努力計算--
13
--正在努力計算--
2

# 通用裝飾器
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力計算--")
result = fn(*args, **kwargs)
return result
return inner

四、多個裝飾器的使用

1. 多個裝飾器的使用示例代碼

def make_div(func):
"""對被裝飾的函數的返回值 div標簽"""
def inner():
return "<div>" + func() + "</div>"
return inner
def make_p(func):
"""對被裝飾的函數的返回值 p標簽"""
def inner():
return "<p>" + func() + "</p>"
return inner
# 裝飾過程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
return "人生苦短"
result = content()
print(result)

代碼說明:

  • 多個裝飾器的裝飾過程是: 離函數最近的裝飾器先裝飾,然後外面的裝飾器再進行裝飾,由內到外的裝飾過程

五、帶有參數的裝飾器

1. 帶有參數的裝飾器介紹

帶有參數的裝飾器就是使用裝飾器裝飾函數的時候可以傳入指定參數,語法格式: @裝飾器(參數,...)

錯誤寫法:

def decorator(fn, flag):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法計算--")
elif flag == "-":
print("--正在努力減法計算--")
result = fn(num1, num2)
return result
return inner
@decorator('+')
def add(a, b):
result = a + b
return result
result = add(1, 3)
print(result)

執行結果:

Traceback (most recent call last):
File "/home/python/Desktop/test/hho.py", line 12, in <module>
@decorator('+')
TypeError: decorator() missing 1 required positional argument: 'flag'

代碼說明:

  • 裝飾器只能接收一個參數,並且還是函數類型。

正確寫法:

在裝飾器外面再包裹上一個函數,讓最外面的函數接收參數,返回的是裝飾器,因為@符號後面必須是裝飾器實例。

# 添加輸出日志的功能
def logging(flag):
def decorator(fn):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法計算--")
elif flag == "-":
print("--正在努力減法計算--")
result = fn(num1, num2)
return result
return inner
# 返回裝飾器
return decorator
# 使用裝飾器裝飾函數
@logging("+")
def add(a, b):
result = a + b
return result
@logging("-")
def sub(a, b):
result = a - b
return result
result = add(1, 2)
print(result)
result = sub(1, 2)
print(result)

 六、類裝飾器

1. 類裝飾器的介紹

裝飾器還有一種特殊的用法就是類裝飾器,就是通過定義一個類來裝飾函數。

類裝飾器示例代碼:

class Check(object):
def __init__(self, fn):
# 初始化操作在此完成
self.__fn = fn
# 實現__call__方法,表示對象是一個可調用對象,可以像調用函數一樣進行調用。
def __call__(self, *args, **kwargs):
# 添加裝飾功能
print("請先登陸...")
self.__fn()
@Check
def comment():
print("發表評論")
comment()

代碼說明:

  • @Check 等價於 comment = Check(comment), 所以需要提供一個init方法,並多增加一個fn參數。
  • 要想類的實例對象能夠像函數一樣調用,需要在類裡面使用call方法,把類的實例變成可調用對象(callable),也就是說可以像調用函數一樣進行調用。
  • call方法裡進行對fn函數的裝飾,可以添加額外的功能。

執行結果:

請先登陸...
發表評論

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved