Python 中的装饰器是一个函数,它接收另一个函数作为参数。传入的函数是要被装饰的函数。装饰器扩展了被装饰函数的行为,而无需实际修改它。
本章中,我们将学习如何使用 Python 装饰器。
定义函数装饰器
Python 中的函数是一等对象。这意味着它们可以像其他数据类型(如数字、字符串或列表等)一样作为参数传递给另一个函数。也可以在另一个函数内部定义一个函数。这样的函数称为嵌套函数。此外,函数也可以返回其他函数。
装饰器函数的典型定义如下:
def decorator(arg_function): 
   def nested_function():
      
      
      arg_function()
   return nested_function
这里是一个普通的 Python 函数:
def function():
   print("hello")
现在你可以通过将其传递给装饰器来装饰此函数以扩展其行为:
function = decorator(function)
如果现在执行这个函数,它将会显示出由装饰器扩展后的输出。
Python 装饰器示例
练习以下示例来理解 Python 装饰器的概念。
示例 1
下面的代码是一个简单的装饰器示例:
def my_function(x):
   print("The number is=", x)
def my_decorator(some_function, num):
   def wrapper(num):
      print("Inside wrapper to check odd/even")
      if num % 2 == 0:
         ret = "Even"
      else:
         ret = "Odd!"
      some_function(num)
      return ret
   print("wrapper function is called")
   return wrapper
no = 10
my_function = my_decorator(my_function, no)
print("It is", my_function(no))
my_function() 只是打印出接收到的数字。然而,通过将其传递给 my_decorator,其行为被修改了。内部函数接收数字并返回它是奇数还是偶数。上述代码的输出为:
wrapper function is called
Inside wrapper to check odd/even
The number is= 10
It is Even
示例 2
一种更优雅的方式来装饰一个函数是在其定义前加上装饰器名前缀 @ 符号。上面的例子改写成这种方式如下:
def my_decorator(some_function):
   def wrapper(num):
      print("Inside wrapper to check odd/even")
      if num % 2 == 0:
         ret = "Even"
      else:
         ret = "Odd!"
      some_function(num)
      return ret
   print("wrapper function is called")
   return wrapper
@my_decorator
def my_function(x):
   print("The number is=", x)
no = 10
print("It is", my_function(no))
Python 标准库定义了以下内置装饰器:
@classmethod 装饰器
classmethod 是一个内置函数。它把一个方法转换成类方法。类方法不同于实例方法。定义在类中的实例方法是由类的对象调用的,该方法隐式接收一个引用 self。另一方面,类方法隐式接收类本身作为第一个参数。
语法
为了声明一个类方法,使用以下装饰器的标记:
class Myclass:
   @classmethod
   def mymethod(cls):
      
@classmethod 的形式如同前面描述的函数装饰器。mymethod 接收对类的引用。它既可以被类也可以被其对象调用。也就是说,Myclass.mymethod 以及 Myclass().mymethod 都是有效的调用。
@classmethod 装饰器示例
让我们通过以下示例来理解类方法的行为:
class counter:
   count = 0
   def __init__(self):
      print("init called by", self)
      counter.count = counter.count + 1
      print("count=", counter.count)
   @classmethod
   def showcount(cls):
      print("called by", cls)
      print("count=", cls.count)
c1 = counter()
c2 = counter()
print("class method called by object")
c1.showcount()
print("class method called by class")
counter.showcount()
在类定义中,count 是一个类属性。__init__() 方法是构造函数,显然它是一个实例方法,因为它接收 self 作为对象引用。每个对象声明都会调用此方法,并使 count 加一。
@classmethod 装饰器将 showcount() 方法转换为类方法,即使是由其对象调用,它也会接收类的引用作为参数。可以看到,即使是 c1 对象调用 showcount,也显示了 counter 类的引用。
它将显示以下输出:
init called by <__main__.counter object at 0x000001D32DB4F0F0>
count= 1
init called by <__main__.counter object at 0x000001D32DAC8710>
count= 2
class method called by object
called by <class '__main__.counter'>
count= 2
class method called by class
called by <class '__main__.counter'>
@staticmethod 装饰器
staticmethod 同样是 Python 标准库中的内置函数。它将一个方法转换为静态方法。静态方法在无论是由类的实例还是类自身调用时都不会接收任何引用参数。在类中声明静态方法使用的标记如下:
class Myclass:
   @staticmethod
   def mymethod():
      
尽管 Myclass.mymethod 以及 Myclass().mymethod 都是有效的调用,但静态方法不会接收任何引用。
@staticmethod 装饰器示例
counter 类被修改如下:
class counter:
   count = 0
   def __init__(self):
      print("init called by", self)
      counter.count = counter.count + 1
      print("count=", counter.count)
   @staticmethod
   def showcount():
      print("count=", counter.count)
c1 = counter()
c2 = counter()
print("class method called by object")
c1.showcount()
print("class method called by class")
counter.showcount()
如同之前所述,在 __init__() 方法内部每个对象声明时 count 类属性都会增加。然而,由于 mymethod 是静态方法,它没有接收 self 或者 cls 参数。因此,类属性 count 的值显示时带有显式的 counter 引用。
上述代码的输出如下:
init called by <__main__.counter object at 0x000002512EDCF0B8>
count= 1
init called by <__main__.counter object at 0x000002512ED48668>
count= 2
class method called by object
count= 2
class method called by class
count= 2
@property 装饰器
Python 的 property() 内置函数是用于访问类的实例变量的接口。@property 装饰器将实例方法转换为同名只读属性的“getter”,并且设置属性的文档字符串为“获取实例变量的当前值”。
您可以使用以下三种装饰器来定义一个属性:
- 
- 
@<property-name>.setter—— 指定属性的设置方法。
- 
@<property-name>.deleter—— 指定删除属性的方法。
由 property() 函数返回的属性对象有 getter、setter 和 delete 方法。
property(fget=None, fset=None, fdel=None, doc=None)
fget 参数是 getter 方法,fset 是 setter 方法。可选地可以有 fdel 作为删除对象的方法,doc 是文档字符串。
语法
property() 对象的 setter 和 getter 也可以用以下语法来赋值:
speed = property()
speed = speed.getter(speed, get_speed)
speed = speed.setter(speed, set_speed)
其中 get_speed() 和 set_speed() 是检索和设置 Car 类中实例变量 speed 的值的实例方法。
上述语句可以通过 @property 装饰器实现。使用装饰器重写 Car 类如下:
@property 装饰器示例
class car:
   def __init__(self, speed=40):
      self._speed = speed
      return
   @property
   def speed(self):
      return self._speed
   @speed.setter
   def speed(self, speed):
      if speed < 0 or speed > 100:
         print("speed limit 0 to 100")
         return
      self._speed = speed
      return
c1 = car()
print(c1.speed) 
c1.speed = 60 
属性装饰器是非常方便且推荐处理实例属性的方法。