Decorators Pattern in Python

Decorators Pattern in Python

装饰器模式(Decorator Pattern)是一种设计模式,它允许你通过将对象放入包装对象中来为原对象添加新的行为。装饰器模式是一种替代继承的技术,它通过一种无需子类化增加功能的方式来扩展类的功能。

使用 decorators,可以在不直接修改源码的情况下修改 function 或 method 的功能,从而让代码更简洁,更 Pythonic

The fundamentals

functions in python

python 中函数是 first-class object,可以赋值给变量、作为参数传递、作为返回值、在运行时动态创建和修改,结合内置的装饰器,可以方便地实现横切关注点,无需额外的 AOP 库。

first-class object

  • being expressible as an anonymous literal value
  • being storable in variables
  • being storable in data structures
  • having an intrinsic identity (independent of any given name)
  • being comparable for equality with other entities
  • being passable as a parameter to a procedure/function
  • being returnable as the result of a procedure/function
  • being constructible at runtime
  • being printable
  • being readable
  • being transmissible among distributed processes
  • being storable outside running processes

如下是使用 Python 中函数特性的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. Assigning Functions to Variables
say_hello = greet
print(say_hello("Alice"))  # Output: Hello, Alice!


# 2. Pass function as an argument
def apply_function(func, value):
    return func(value)

def uppercase(text):
    return text.upper()

result = apply_function(uppercase, "hello")
print(result)  # Output: HELLO

# 3. Return function from another function (Nested Function)
def make_multiplier(n):
    def multiplier(x):
        return x * n
    return multiplier

double = make_multiplier(2)
print(double(5))  # Output: 10

Simple Demo

结合以上特性,可以实现一个简单的装饰器,实现计时功能

Tips: 建议使用 functools.wraps 保留原函数的元信息,如__name__、__doc__

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
30
31
from functools import wraps
from time import time


def simple_decorator(func):
    @wraps(func)   # 此注解用于保留func的元信息
    def wrapper(*args, **kwargs):
        print(f"function <{func.__name__}> is called")
        start_time = time()
        result = func(*args, **kwargs)
        print(f"Function execution time: {time() - start_time}")
        return result

    return wrapper


@simple_decorator
def calculate():
    sum = 0
    for i in range(1000000):
        sum += i
    print(sum)
# 上述代码等价于
# @simple_decorator syntax 等价于 calculate = simple_decorator(calculate)

calculate()

[OUTPUT]
function <calculate> is called
499999500000
Function execution time: 0.16027212142944336

Chaining Decorators

Python 允许使用多个装饰器,可以通过 @decorator1 @decorator2@decoratorN 的方式来实现

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
30
31
32
33
34
35

from functools import wraps


def star(func):
	@wraps(func)
	def wrapper(*args, **kwargs):
		print("*" * 30)
		func(*args, **kwargs)
		print("*" * 30)

	return wrapper

def hyphen(func):
	@wraps(func)
	def wrapper(*args, **kwargs):
		print("-" * 30)
		func(*args, **kwargs)
		print("-" * 30)

	return wrapper

@star
@hyphen
def greet(name):
	print(f"Hello, {name}!")

greet("Python")

[OUTPUT]
******************************
------------------------------
Hello, Python!
------------------------------
******************************

Best Practices(?)

  • Parameter Validation Decorators
  • Method Routing
  • Caching and Memoization

@lru_cache

1
2
3
4
5
6
7
8
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

getter/setter

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
class Person:
	def __init__(self, name, age):
		self._name = name
		self._age = age

	@property
	def name(self):
		return self._name

	@name.setter
	def name(self, value):
		self._name = value

	@property
	def age(self):
		return self._age

	@age.setter
	def age(self, value):
		self._age = value

person = Person("Alice", 25)
print(person.name)  # Output: Alice
person.name = "Bob"
print(person.name)  # Output: Bob

Ref

What are “first-class” objects? —— StackOverFlow

作者

GnixAij

发布于

2024-12-03

更新于

2025-01-14

许可协议

评论