深入浅出 Python Decorators

原文


装饰器 属于 metaprogramming 的一种,是在编译时一段程序尝试去修改另外一段程序的部分内容。

基础知识

在 python 中,everything 都是 objects。
Names 只是 tags bound to these objects。
甚至 classes,functions 都是 objects。
例如:

a = math.sin,则a就用了同sin一样的功能;
然后math.sin甚至可以指向其他函数,例如math.sin = func,则math.sin就变成了 func 函数的功能
而此时a仍然具有sin函数的功能

函数可以作为参数传递给其他函数,例如map, filter, reduce.
这些接受函数作为参数的函数被称为 higher order functions.

函数也可以作为返回值被返回,例如在上一节中提到的 Closure。

回到 Decorators

functions 和 methods 都被称为 callable,因为他们都可以被执行。
实际上,任何 object,只要实现了__call__()方法,都是一个 callable
所以,Decorator 就是一个返回 callable 的 callable。
Decorator 使用函数作为参数,增加一些功能做成一个新的函数,然后返回它。

def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner

def ordinary():
    print("I am ordinary")

>>> ordinary()
I am ordinary

>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary

在上面的例子中,make_pretty()就是一个Decorator。
Decorator 就像一个 wrapper 一样。
Decorator 就好像把传入的函数 “decorate/装饰” 了一番,然后又吐了出来。

@Decorator

python 中提供了一个更简单的使用 decorator 的方法,就是@符号:

@make_pretty
def ordinary():
    print("I am ordinary")

就等同于

def ordinary():
    print("I am ordinary")

ordinary = make_pretty(ordinary)

注意,下面的例子中,重新赋值后的 ordinary 已经不是 def 定义的ordinary 了,而是一个经过 decorator 修饰的 ordinary。
因此上面的例子中,@符号直接创造了经过修饰的 ordinary,省去了中间的形式。

为带参数的函数进行“装饰”

例子

def divide(a, b):
    return a/b

上面的函数显然有缺陷,在 b == 0 时会 raise ZeroDivisionError,因此我们可以对之进行“装饰”:

def smart_divide(func):
   def inner(a,b):
      print("I am going to divide",a,"and",b)
      if b == 0:
         print("Whoops! cannot divide")
         return

      return func(a,b)
   return inner

@smart_divide
def divide(a,b):
    return a/b

经过修饰后,divide在当 b == 0 时会警告,变成了一个没有返回值的函数(返回 None);在b != 0 时返回正常的结果,又是一个有返回值的函数。
当使用者调用 divide(n / m)时,实际上是在调用一个内部函数 inner(n / m)
而该内部函数使用了原始的、未被“修饰”的函数,即作为参数传入的 func,即写在@smart_divide下面的那个形式。

通用 decorator

你会发现,实际上inner() 函数使用了和原始的被修饰函数同样的参数。根据这个原理,我们可以写出更通用的格式:

def works_for_all(func):
    def inner(*args, **kwargs):
        print("I can decorate any function")
        return func(*args, **kwargs)
    return inner

那么这个 decorator 可以“修饰”任何函数。

Chaining Decorators

被“修饰”的函数可以再次被“修饰”,就成了 decorator 嵌套:

def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner

def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner

@star
@percent
def printer(msg):
    print(msg)
printer("Hello")

输出结果是:

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

注意,@star 和 @percent 的前后顺序很重要!不然就是不一样的结果。

带有参数的 decorator

有时候你会看到下面的形式:

@decorator(param)
def func():
    pass

不要怕,根据其原理,那么就相当于:

def func():
    pass

func = decorator(param)(func)

可见,decorator 本身就是一个返回 Decorator 的 Decorator 函数。其形式诸如

def decorator(argument):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            function(*args, **kwargs)
            more_funny_stuff()
        return wrapper
    return real_decorator

几个跟 Class 相关的常用 Decorator

1. @classmethod 和 @staticmethod

  • 在类中,normal method 也称为 instance method,定义 instance method 时,需要默认给定一个 self 参数,代表了实例本身。
class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

>>> date1 = Date(6, 11, 2017)


  • 在类中,还有一类 method 称为 class method,定义 class method 时,需要默认给定一个 cls 参数,代表了类本身。
...
    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

date2 = Date.from_string('11-09-2012')

因为在 python 中没有 重载/Overloading,所以不能用 date2 = Date('11-09-2012') 的形式定义另外一个构造函数,因此需要类提供新的方法来进行“构造”。
在上面的例子中,@classmethod 为该类引入了一个新方法 from_string
@classmethod 修饰符是必须的,否则,如果没有这个修饰符,即使你为 from_string 写了 cls 作为一参,它实际上仍然是意义上的 self,并且你只能通过 = Date().from_string(... 的形式使用它。

  • 在类中,还有一类 method 是 static method,它对应于 C++ 中的 static method,其实就是一个普通的函数,只不过被包含在了类中,此时”类”的作用仅仅是作为一个 naming space.
...
    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

# usage:
is_date = Date.is_date_valid('11-09-2012')

同样,该方法不需要任何类的实例就可以直接使用。
当然,对于 class method 和 static method,你通过类的实例使用也是没问题的,即 is_date = Date().is_date_valid('11-09-2012') 也合法,但没人这样用,因为它使得 class methodstatic method 的含义变得模糊了。

2. @property

见下一节

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python decorators是一种用于修改或增强函数功能的语法结构。它们允许开发者在不修改原始函数代码的情况下,通过在函数定义之前使用特殊符号(@)和装饰器函数来对函数进行包装或修饰。装饰器函数接受被装饰函数作为参数,并可以在原始函数执行之前或之后添加额外的逻辑或功能。这种技术可以用来实现缓存、日志记录、身份验证等功能。 Python decorators的使用方法可以根据具体需求进行定义和实现。常见的方法包括使用装饰器函数、使用类作为装饰器、使用带参数的装饰器等。装饰器函数是最常见的一种方式,它接受一个函数作为参数并返回一个新的函数,新函数会替换原始函数。这样,在调用原始函数时,实际上是调用了被装饰的函数,从而在不修改原始函数的情况下添加了额外的功能。 除了使用Python内置的装饰器语法,还可以使用第三方库来简化装饰器的编写和使用。例如,可以使用decorator模块来定义和使用装饰器。这个模块提供了一种更简洁的语法,可以直接在函数定义之前使用@decorator语法来应用装饰器。该模块的使用方法如下所示: ```python from decorator import decorator @decorator def hint(func, *args, **kwargs): print('{} is running'.format(func.__name__)) return func(*args, **kwargs) ``` 上述代码定义了一个名为hint的装饰器函数,它接受一个函数作为参数,并在函数执行之前打印出函数名。然后,通过在函数定义之前使用@hint语法,将装饰器应用到目标函数上。这样,在调用目标函数时,实际上会先执行装饰器函数内部的逻辑,然后再执行目标函数本身的逻辑。 总结来说,Python decorators是一种用于修饰函数的语法结构,可以通过装饰器函数在不修改原始函数代码的情况下增强函数功能。它可以通过Python内置的装饰器语法或第三方库来实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Python Decorator](https://blog.csdn.net/weixin_30951231/article/details/96490117)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Python系列之装饰器(decorator)](https://blog.csdn.net/ikxyang/article/details/121995824)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值