Python-装饰器-0.常见装饰器汇总.md

什么是装饰器

装饰器其实就是一个以函数或对象作为参数并返回一个替换函数或对象的可执行函数,即装饰器是一个函数,它以函数作为参数,返回另一个函数。

可以先来看一个装饰器修饰函数的例子,理解装饰器的作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 定义一个装饰器函数,虽然没有任何用处,但是它确实是一个最简单的装饰器,并且可以用的很好。
def warp(obj):
return obj

# 使用装饰器的方法1:
@warp # 等价于 foo = warp(foo)
def foo():
print('hello decorator!')

foo() # => hello decorator!

# 使用装饰器的方法2:
def foo():
print('hello decorator!')

foo = warp(foo)
foo() # => hello decorator!

再函数定义前添加 @warp 和将 foo = warp(foo) 一个使用装饰器函数处理后的新函数重新定义到原命名是一样的。
通过最简单的代码示例,我们可以理解,装饰器其实就是接受了一个函数(对象),并且返回了一个函数(对象)的函数(可调用对象)。

上面是函数的例子,接下来我们可以看一个用装饰器修饰类的例子:

1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个装饰器
def warp(obj):
obj.name = 'python'
return obj

# 使用装饰器装饰一个类(Bar):
@warp # => Bar = warp(Bar)
class Bar(object):
def __init__(self):
pass

print(Bar.name) # => python

在这个示例中,我们通过一个装饰器,对一个类的属性进行了默认配置。

通过上述两个示例,我们可以理解装饰器的作用,它可以让我们再不用对原类/函数进行任何修改的情况下,给类增加新的功能。

函数相关装饰器

@atexit.register

`@atexit.register `装饰器用于注册要在程序终止时执行的函数。该函数可用于在程序即将退出时执行任何任务,无论是由于正常执行还是意外错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import atexit

# Register the exit_handler function
@atexit.register
def exit_handler():
print("退出程序。可以在此处执行清理任务。")

# Rest of the program
def main():
print("main函数内部。")
# Your program logic goes here.

if __name__ == "__main__":
main()

@enum.unique

@enum.unique 装饰器位于 enum 模块中,用于确保枚举的所有成员的值是唯一的。这有助于防止意外创建具有相同值的多个枚举成员,这可能会导致混乱和错误。如果发现重复值,则会引发 ValueError。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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}")

在上面的实现中,“BUS”和“MOTORCYCLE”具有相同的值“3”。结果,@unique 装饰器引发 ValueError 并显示一条消息,指示已找到重复值。我们不能多次使用相同的 key,也不能将相同的值分配给不同的成员。通过这种方式,它有助于防止多个枚举成员出现重复值。

@singledisptach

@singledisptach 装饰器用于创建通用函数。它允许我们定义具有相同名称但不同参数类型的函数的不同实现。当我们希望代码针对不同的数据类型表现不同时,它特别有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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])
1
2
3
4
Received an integer: 39
Received a float: 3.19
Received a string: Hello World!
Received a sequence: [2, 4, 6]

在上面的实现中,我们首先使用@singledisptach装饰器开发了通用函数display_info(),然后分别注册了其int、float、string和list的实现。输出显示了不同数据类型的 display_info() 的工作情况。

类相关装饰器

@property

可以将对象内定义的方法转换为属性(不可变更值),同时调用的方法发生变化(方法的调用需要后面添加”()”,属性不能添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
DataSet(object):
@property
def method_with_property(self): ##含有@property
return 15
def method_without_property(self): ##不含@property
return 15

l = DataSet()
print(l.method_with_property) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。
print(l.method_without_property()) #没有加@property , 必须使用正常的调用方法的形式,即在后面加()

class DataSet(object):
def __init__(self):
self._images = 1
self._labels = 2 #定义属性的名称
@property
def images(self): #方法加入@property后,这个方法相当于一个属性,这个属性可以让用户进行使用,而且用户有没办法随意修改。
return self._images
@property
def labels(self):
return self._labels
l = DataSet()

#用户进行属性调用的时候,直接调用images即可,而不用知道属性名_images,因此用户无法更改属性,从而保护了类的属性。
print(l.images) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。

@classmethod

classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数可以来调用类的属性,类的方法,实例化对象等。

1
2
3
4
5
6
7
8
9
10
11
class A(object):
bar = 1
def func1(self):
print ('foo')
@classmethod
def func2(cls):
print ('func2')
print (cls.bar)
cls().func1() # 调用 foo 方法

A.func2() # 不需要实例化

@staticmethod

python staticmethod 返回函数的静态方法(该方法不强制要求传递参数)。如下的代码,实例声明了静态方法 *toDashDate()* ,从而可以不实例化调用该方法 Dates.toDashDate(),当然也可以实现实例化使用 Dates().toDashDate()。 静态方法的用例有限,因为与类方法或类中的任何其他方法一样,它们无法访问类本身的属性。但下述事一种情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Dates:
def __init__(self, date):
self.date = date

def getDate(self):
return self.date

@staticmethod
def toDashDate(date):
return date.replace("/", "-")

date = Dates("15-12-2016")
dateFromDB = "15/12/2016"
dateWithDash = Dates.toDashDate(dateFromDB) # 不需要实例化,直接调用类函数

if(date.getDate() == dateWithDash):
print("Equal")
else:
print("Unequal")

在这里,我们有一个 Dates 类,它仅适用于带破折号的日期。然而,在我们之前的数据库中,所有日期都以斜杠表示。
为了将斜杠日期转换为破折号日期,我们在 Dates 中创建了一个实用函数 toDashDate 。
它是一个静态方法,因为它不需要访问 Dates 本身的任何属性,只需要参数。
我们还可以在类外部创建 toDashDate ,但由于它仅适用于日期,因此将其保留在 Dates 类中是合乎逻辑的。

Function相关的装饰器

-------------本文结束感谢您的阅读-------------