Python中的装饰器是什么?
在Python的开发过程中,经常会需要修改一个已经存在的函数的功能而不改变函数代码,这时候就可以用到Python中的装饰器(Decorator)。装饰器本质上是一个可以在不改变原函数代码的情况下为函数添加新的功能的一个标准化语法,也是Python编程中一个非常重要的概念。本文将重点介绍Python中的装饰器的定义、应用及代码示例。
阅读更多:Python 教程
装饰器是什么?
装饰器本质上是一个函数或类,它可以接受一个或多个函数为参数,然后向该函数添加新的功能,并返回一个使用该功能的函数。语法如下:
def decorator(func):
def wrapper(*args, **kw):
# 添加新的功能
return func(*args, **kw)
return wrapper
@decorator
def func():
# 原函数功能
此处以一个实际的示例为例,修改一个“Hello, world!”函数,输出语句添加时间戳:
import time
def timeit(func):
def wrapper():
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())), end=" ")
return func()
return wrapper
@timeit
def hello():
print("Hello, world!")
hello()
运行结果为:
2022-08-30 10:10:57 Hello, world!
上述示例中,时间戳功能由装饰器timeit
实现。
应用装饰器
在Python中,装饰器主要有两种实现方式:函数装饰器和类装饰器。
函数装饰器
函数装饰器是一种将新的功能添加到一个或多个函数的便捷方法。在使用函数装饰器时,可以使用‘@’操作符将一个函数作为装饰器应用到某个函数上。
以带有函数装饰器的代码为例,展示Python中函数装饰器的应用方法:
def timeit(func):
def wrapper():
start = time.clock()
result = func()
end = time.clock()
print('used:', end - start)
return result
return wrapper
@timeit
def foo():
time.sleep(0.6)
print('in foo()')
@timeit
def bar():
time.sleep(0.3)
print('in bar()')
bar()
foo()
运行结果为:
in bar()
used: 0.3070769999999998
in foo()
used: 0.6052540000000002
类装饰器
装饰器也可以是一个类,使用方式与函数装饰器类似。类装饰器实现起来稍微有些复杂,但相对地它们允许更多自定义功能和丰富的设计。
class timeit:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
start = time.clock()
result = self._func(*args, **kwargs)
end = time.clock()
print('used:', end - start)
return result
@timeit
def foo():
time.sleep(0.6)
print('in foo()')
@timeit
def bar():
time.sleep(0.3)
print('in bar()')
bar()
foo()
运行结果与函数装饰器相同。
多个装饰器的使用
当需要在一个函数中应用多个装饰器时的代码实现。
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print(hello())
运行结果为:
<b><i>hello world</i></b>
上述示例中,使用了makebold和makeitalic两个装饰器同时对hello函数进行装饰,使得函数返回值被加粗和变斜体。
装饰器的参数
有时候,我们需要在装饰器中添加额外的参数。例如,为了用装饰器来标记函数名称:
def name_decorator(name):
def wrapper(function):
def wrapped(*args, **kwargs):
print("Function name: %s" % name)
return function(*args, **kwargs)
return wrapped
return wrapper
@name_decorator("hello_function")
def hello():
print("Hello, world!")
hello()
运行结果为:
Function name: hello_function
Hello, world!
类装饰器的参数
除了函数装饰器可以使用参数,类装饰器也有同样的特性。
class NameDecorator:
def __init__(self, name):
self.name = name
def __call__(self, function):
def wrapped(*args, **kwargs):
print("Function name: %s" % self.name)
return function(*args, **kwargs)
return wrapped
@NameDecorator("hello_function")
def hello():
print("Hello, world!")
hello()
运行结果相同。
带参数和不带参数的装饰器
装饰器可以有参数,也可以没有参数。没有参数的装饰器可通过一个简单的“if”语句实现。
def my_decorator(func):
if not True: # 此处加入条件
return func
def wrapper():
print('Something is happening before the function is called')
func()
print('Something is happening after the function is called')
return wrapper
@my_decorator # 无条件下,不会执行修改
def say_hello():
print('Hello!')
say_hello()
运行结果为:
Hello!
不同作用域间的装饰器
装饰器可以用于全局,也可以用于局部。
def method_decorator(method):
def wrapper(self, *args, **kwargs):
print("method_decorator: before method_1")
result = method(self, *args, **kwargs)
print("method_decorator: after method_1")
return result
return wrapper
class MyClass:
@method_decorator
def method_1(self):
print("method_1")
def method_2(self):
print("method_2")
m = MyClass()
m.method_1()
m.method_2()
运行结果为:
method_decorator: before method_1
method_1
method_decorator: after method_1
method_2
内置装饰器
Python自带了多个内置装饰器,比如@staticmethod、@classmethod、@property等。下面将展示类的装饰器用法。
class C:
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
c = C()
c.x = "hello world"
print(c.x)
运行结果为:
hello world
上述示例中,使用的@property、@setter和@deleter装饰器将x属性转换为一个可读、可写甚至可删除的文件属性。
结论
Python的装饰器是源代码简洁、优雅的方式,使得代码的重复使用更加容易,同时还能提高代码的可读性和可维护性。通过本文的学习,您应该已经了解了装饰器的概念和适用场景,并对Python中常见的装饰器语法有一定的认识,可以在开发过程中利用装饰器解决复杂的问题。在实际应用中,您还可以通过自定义装饰器来实现更加个性化的功能。
需要注意的是,如果不谨慎使用装饰器,可能会导致代码变得难以理解和调试。因此,应该谨慎使用装饰器,并在实际应用中根据具体情况进行选择使用。