Python装饰器(进阶)
到目前为止,您已经了解了如何创建简单的装饰器。您已经很好地理解了什么是装饰器以及它们是如何工作的。从这篇文章中休息一下,把你学到的东西都练习一下。
在本教程的第二部分中,我们将探索更高级的特性,包括如何使用以下特性:
- 修饰符类
- Several decorators on one function
- Decorators with arguments
- Decorators that can optionally take arguments
- Stateful decorators
- Classes as decorators
修饰类
在类上使用装饰器有两种不同的方法。第一个非常接近于您已经完成的函数:您可以修饰类的方法。这是当初引入装饰器的动机之一。
一些常用的装饰器甚至是Python中内置的,它们是@classmethod、@staticmethod和@property。@classmethod和@staticmethod修饰符用于定义类名称空间中的方法,这些方法不连接到该类的特定实例。@property装饰器用于自定义类属性的getter和setter。展开下面的框,查看使用这些装饰器的示例。
内置修饰器例子:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""Get value of radius"""
return self._radius
@radius.setter
def radius(self, value):
"""Set radius, raise error if negative"""
if value >= 0:
self._radius = value
else:
raise ValueError("Radius must be positive")
@property
def area(self):
"""Calculate area inside circle"""
return self.pi() * self.radius**2
def cylinder_volume(self, height):
"""Calculate volume of cylinder with circle as base"""
return self.area * height
@classmethod
def unit_circle(cls):
"""Factory method creating a circle with radius 1"""
return cls(1)
@staticmethod
def pi():
"""Value of π, could use math.pi instead though"""
return 3.1415926535
在例子中:
- . columnder_volume()是一种常规方法。
- radius是一个可变的属性:它可以被设置为一个不同的值。然而,通过定义setter方法,我们可以进行一些错误测试,以确保它没有设置为无意义的负数。属性作为属性访问,不使用括号。
- area是一个不可变的属性:没有.setter()方法的属性是无法更改的。即使将其定义为方法,也可以将其检索为没有括号的属性。
- .unit_circle()是一个类方法。它不局限于一个特定的圆实例。类方法通常用作可以创建类的特定实例的工厂方法。
- .pi()是一个静态方法。它实际上并不依赖于Circle类,只是它是其名称空间的一部分。可以对实例或类调用静态方法。
运行例子,
>>> c = Circle(5)
>>> c.radius
5
>>> c.area
78.5398163375
>>> c.radius = 2
>>> c.area
12.566370614
>>> c.area = 100
AttributeError: can't set attribute
>>> c.cylinder_volume(height=4)
50.265482456
>>> c.radius = -1
ValueError: Radius must be positive
>>> c = Circle.unit_circle()
>>> c.radius
1
>>> c.pi()
3.1415926535
>>> Circle.pi()
3.1415926535
自定义例子:
使用之前的@debug 和 @timer ,在Python装饰器入门中。
from decorators import debug, timer
class TimeWaster:
@debug
def __init__(self, max_num):
self.max_num = max_num
@timer
def waste_time(self, num_times):
for _ in range(num_times):
sum([i**2 for i in range(self.max_num)])
使用这个类你就能看到修饰的效果:
>>> tw = TimeWaster(1000)
Calling __init__(<time_waster.TimeWaster object at 0x7efccce03908>, 1000)
'__init__' returned None
>>> tw.waste_time(999)
Finished 'waste_time' in 0.3376 secs
类修饰的另一种方法
在类上使用装饰器的另一种方法是装饰整个类。例如,这是在Python 3.7的新数据类模块中完成的:
from dataclasses import dataclass
@dataclass
class PlayingCard:
rank: str
suit: str
语法的含义类似于函数修饰符。在上面的例子中,您可以通过编写PlayingCard = dataclass(PlayingCard)来完成装饰。
就是把一个class扔给修饰器,然后扔出来一个新的class。
类修饰符的一个常见用途是作为元类的一些用例的简单替代。在这两种情况下,您都在动态地更改类的定义。