編寫優(yōu)雅代碼的八個內(nèi)置Python裝飾器
譯文譯者 | 布加迪
審校 | 重樓
Python具有語法清晰易讀的優(yōu)點,是一種廣泛使用的高級編程語言。Python是為確保易用性而設(shè)計的,注重簡潔性和降低程序的維護(hù)成本。它隨帶一個廣泛的庫,減少了開發(fā)人員從頭開始編寫代碼的需要,并提高了開發(fā)人員的生產(chǎn)力。Python的一項有助于確保代碼優(yōu)雅的強大特性是裝飾器(decorator)。
Python裝飾器的定義
在Python中,decorator是一個函數(shù),允許您修改另一個函數(shù)的行為,而不改變其核心邏輯。它接受另一個函數(shù)作為參數(shù),并返回功能得到擴(kuò)展的函數(shù)。這樣一來,您就可以使用裝飾器為現(xiàn)有函數(shù)添加一些額外的邏輯,只需要幾行代碼就可以提高可重用性。我們在本文中將介紹8個內(nèi)置的Python裝飾器,它們可以幫助您編寫更優(yōu)雅、更易于維護(hù)的代碼。
1. @atexit.register
@atexit.register裝飾器用于注冊在程序結(jié)束時執(zhí)行的函數(shù)。該函數(shù)可用于在程序即將退出時執(zhí)行任何任務(wù),無論是由于正常執(zhí)行還是由于意外錯誤。
例子:
import atexit
# Register the exit_handler function
@atexit.register
def exit_handler():
print("Exiting the program. Cleanup tasks can be performed here.")
# Rest of the program
def main():
print("Inside the main function.")
# Your program logic goes here.
if __name__ == "__main__":
main()
輸出:
Inside the main function.
Exiting the program. Cleanup tasks can be performed here.
在上面的實現(xiàn)中,@atexit.register在函數(shù)定義上面提及。它將exit_handler()函數(shù)定義為退出函數(shù)。實際上,這意味著每當(dāng)程序到達(dá)其終止點,無論是通過正常執(zhí)行還是由于意外錯誤導(dǎo)致過早退出,都將調(diào)用exit_handler()函數(shù)。
2. @dataclasses.dataclass
@dataclasses.dataclass是一個功能強大的裝飾器,用于自動為“__init__”、“__repr__”等其他類生成常見的特殊方法。由于不需要編寫用于初始化和比較類的實例的樣板方法,它可以幫助您編寫更干凈、更簡潔的代碼。它還可以通過確保在整個代碼庫中一致地實現(xiàn)常見的特殊方法來幫助防止錯誤。
例子:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
point = Point(x=3, y=2)
# Printing object
print(point)
# Checking for the equality of two objects
point1 = Point(x=1, y=2)
point2 = Point(x=1, y=2)
print(point1 == point2)
輸出:
Point(x=3, y=2)
True
@dataclass裝飾器應(yīng)用于Point類定義之上,通知Python使用默認(rèn)行為來生成特殊方法。這會自動創(chuàng)建__init__方法,該方法在對象實例化時初始化類屬性,比如x和y。因此,可以在不需要顯式編碼的情況下構(gòu)造像point這樣的實例。此外,負(fù)責(zé)提供對象字符串表示的__repr__方法也會自動加以調(diào)整。這確保了在打印輸出對象(比如point)時,它會生成清晰而有序的表示,比如輸出:point (x=3, y=2)中所示。
此外,兩個實例point1和point2之間的相等性比較(==)生成True。這值得注意,因為默認(rèn)情況下,Python根據(jù)內(nèi)存位置來檢查是否相等。然而,在數(shù)據(jù)類對象上下文中,相等性取決于其中含有的數(shù)據(jù)。這是由于@dataclass裝飾器生成了一個__eq__方法,該方法檢查對象中存在的數(shù)據(jù)是否相等,而不是檢查相同的內(nèi)存位置。
3. @enum.unique
@enum.unique裝飾器位于enum模塊中,用于確保枚舉中所有成員的值是唯一的。這有助于防止意外創(chuàng)建具有相同值的多個枚舉成員,不然會導(dǎo)致混淆和錯誤。如果發(fā)現(xiàn)重復(fù)的值,拋出ValueError(值錯誤)。
例子:
from enum import Enum, unique
@unique
class VehicleType(Enum):
CAR = 1
TRUCK = 2
MOTORCYCLE = 3
BUS = 4
# Attempting to create an enumeration with a duplicate value will raise a ValueError
try:
@unique
class DuplicateVehicleType(Enum):
CAR = 1
TRUCK = 2
MOTORCYCLE = 3
# BUS and MOTORCYCLE have duplicate values
BUS = 3
except ValueError as e:
print(f"Error: {e}")
輸出:
Error: duplicate values found in : BUS -> MOTORCYCLE
在上述實現(xiàn)中,“BUS”和“MOTORCYCLE”有相同的值“3”。因此,@unique裝飾器拋出值錯誤,顯示一條消息,表明發(fā)現(xiàn)了重復(fù)的值。您既不能多次使用相同的鍵,也不能將相同的值分配給不同的成員。通過這種方式,它有助于防止多個枚舉成員出現(xiàn)重復(fù)值。
4. @partial
partial裝飾器是一個強大的工具,用于創(chuàng)建偏函數(shù)。偏函數(shù)允許您預(yù)先設(shè)置一些原始函數(shù)的參數(shù),然后用已經(jīng)填寫的這些參數(shù)生成一個新的函數(shù)。
例子:
from functools import partial
# Original function
def power(base, exponent):
return base ** exponent
# Creating a partial function with the exponent fixed to 2
square = partial(power, expnotallow=2)
# Using the partial function
result = square(3)
print("Output:",result)
輸出:
Output: 9
在上面的實現(xiàn)中,我們有一個函數(shù)“power”,它接受兩個參數(shù)“base”和“exponent”,并返回指數(shù)次冪基數(shù)的結(jié)果。我們使用原始函數(shù)創(chuàng)建了一個名為“square”的偏函數(shù),其中指數(shù)被預(yù)先設(shè)置為2。這樣一來,我們可以使用partial裝飾器擴(kuò)展原始函數(shù)的功能。
5. @singledispatch
@singledisptach裝飾器用于創(chuàng)建泛型函數(shù)。它允許您定義名稱相同但參數(shù)類型各異的函數(shù)的不同實現(xiàn)。當(dāng)您希望代碼針對不同的數(shù)據(jù)類型有不同的行為時,這個裝飾器就特別有用。
例子:
from functools import singledispatch
# Decorator
@singledispatch
def display_info(arg):
print(f"Generic: {arg}")
# Registering specialized implementations for different types
@display_info.register(int)
def display_int(arg):
print(f"Received an integer: {arg}")
@display_info.register(float)
def display_float(arg):
print(f"Received a float: {arg}")
@display_info.register(str)
def display_str(arg):
print(f"Received a string: {arg}")
@display_info.register(list)
def display_sequence(arg):
print(f"Received a sequence: {arg}")
# Using the generic function with different types
display_info(39)
display_info(3.19)
display_info("Hello World!")
display_info([2, 4, 6])
輸出:
Received an integer: 39
Received a float: 3.19
Received a string: Hello World!
Received a sequence: [2, 4, 6]
在上面的實現(xiàn)中,我們先使用@singledisptach裝飾器開發(fā)了泛型函數(shù)display_info(),然后分別為整數(shù)、浮點、字符串和列表注冊了其實現(xiàn)。輸出顯示了display_info()針對不同數(shù)據(jù)類型的工作機理。
6. @classmethod
@classmethod是一個裝飾器,用于在類中定義類方法。類方法綁定到類而不是綁定到類的對象。靜態(tài)方法與類方法的主要區(qū)別在于它們與類狀態(tài)的交互。類方法可以訪問并修改類狀態(tài),而靜態(tài)方法無法訪問類狀態(tài)、獨立操作。
例子:
class Student:
total_students = 0
def __init__(self, name, age):
self.name = name
self.age = age
Student.total_students += 1
@classmethod
def increment_total_students(cls):
cls.total_students += 1
print(f"Class method called. Total students now: {cls.total_students}")
# Creating instances of the class
student1 = Student(name="Tom", age=20)
student2 = Student(name="Cruise", age=22)
# Calling the class method
Student.increment_total_students() #Total students now: 3
# Accessing the class variable
print(f"Total students from student 1: {student1.total_students}")
print(f"Total students from student 2: {student2.total_students}")
輸出:
Class method called. Total students now: 3
Total students from student 1: 3
Total students from student 2: 3
在上面的實現(xiàn)中,Student類擁有total_students這個類變量。@classmethod裝飾器用于定義increment_total_students()類方法,以增加total_students變量。每當(dāng)我們創(chuàng)建Student類的實例時,學(xué)生總數(shù)就增加1。我們創(chuàng)建了這個類的兩個實例,然后使用類方法將total_students變量修改為3,這個類的實例也反映了這點。
7. @staticmethod
@staticmethod裝飾器用于在類中定義靜態(tài)方法。靜態(tài)方法是無需創(chuàng)建類實例即可調(diào)用的方法。靜態(tài)方法常常用于不需要訪問與對象相關(guān)的參數(shù),與整個類更加相關(guān)。
例子:
class MathOperations:
@staticmethod
def add(x, y):
return x + y
@staticmethod
def subtract(x, y):
return x - y
# Using the static methods without creating an instance of the class
sum_result = MathOperations.add(5, 4)
difference_result = MathOperations.subtract(8, 3)
print("Sum:", sum_result)
print("Difference:", difference_result)
輸出:
Sum: 9
Difference: 5
在上面的實現(xiàn)中,我們使用@staticmethod為類“MathOperations”定義了一個靜態(tài)方法add()。我們添加了兩個數(shù)字“4”和“5”,結(jié)果是“9”,沒有創(chuàng)建類的任何實例。同樣,將“8”和“3”兩個數(shù)字相減得到“5”。這樣一來,就可以生成靜態(tài)方法,以執(zhí)行不需要實例狀態(tài)的效用函數(shù)。
8. @ property
@property裝飾器用于定義類屬性的getter方法。getter方法是返回屬性值的方法。這種方法用于數(shù)據(jù)封裝,指定誰可以訪問類或?qū)嵗脑敿?xì)信息。
例子:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
# Getter method for the radius.
return self._radius
@property
def area(self):
# Getter method for the area.
return 3.14 * self._radius**2
# Creating an instance of the Circle class
my_circle = Circle(radius=5)
# Accessing properties using the @property decorator
print("Radius:", my_circle.radius)
print("Area:", my_circle.area)
輸出:
Radius: 5
Area: 78.5
在上面的實現(xiàn)中,類“Circle”有一個屬性“radius”。我們使用@property為半徑和面積設(shè)置了getter方法。它為類的用戶提供了一個干凈一致的接口來訪問這些屬性。
結(jié)語
本文重點介紹了一些最通用和最實用的裝飾器,您可以使用它們提高代碼的靈活性和可讀性。這些修飾器讓您可以擴(kuò)展原始函數(shù)的功能,使原始函數(shù)更富條理性,更不容易出錯。它們如同神奇的魔法棒,使您的Python程序看起來整潔、運行起來順暢。
原文標(biāo)題:8 Built-in Python Decorators to Write Elegant Code,作者:Kanwal Mehreen
鏈接:https://www.kdnuggets.com/8-built-in-python-decorators-to-write-elegant-code