Python 爬阶梯 | 语法糖是什么玩意?Python中的函数高级用法——装饰器

通过前面的一些讲解,相信你对 Python 的函数用法有一个大致的认识了,我们知道, Python 中的函数可以被当做参数进行调用,函数中的参数是如何传递的,也知道函数的闭包了,那么接下来我就可以跟你说说函数的比较高级的用法,也就是装饰器。

如果你还对函数的概念有些模糊,可以看下咱们往期关于函数相关的内容再来看这一篇,这样会比较好消化一些。

其实就是想告诉你一个 Google 的搜索技巧

好了。

看到有 b 友对一些方法的定义感到疑惑:为啥在方法上面要各种 @ ?

类似这样:

这是你最常见的一些玩意了吧,它们都是啥意思,以及怎么去定义使用呢?

那么,接下来我就尽量通熟易懂的跟你聊聊这些玩意。

在此之前,我们先看下 Python 中的函数,它的定义和调用可以如此简单:

其实,在 Python 里,一个函数可以是一个对象,一个函数也可以被作为一个「参数」进行传递,像这样:

我们这里定义的函数 「func_1」,直接返回的是「传入进来的函数执行了一波」。

这样显然没有什么意义,更多时候,我们是希望传入的函数在执行的前后可以做一些相关的额外的操作,像这样:

但这里的 「func_1」并没有 return,这就会导致一个这样的 Error:

我们本想拿到函数的引用,然后去执行,可是 func_1 并没有返回任何东西,所以我们第七行得到的 my_func 实际上是 None。

想要解决这样的尴尬,我们可以定义一个内部方法,然后返回这个引用,像这样:

简单说一下这里的执行流程,当你执行第十行 「my_func = func_1(say)」 的时候,这时候 say 方法会作为参数传递给 func_1 函数,而这个函数直接返回了 「inner_func」,那么这时候 my_func 指向的就是 inner_func:

所以我们这时候执行 「my_func()」的就可以执行到内部方法定义的东西。

看到这里,你会发现,我们所定义的 「func_1」实际上就是为了改变(优化)传入进来的函数的执行结果,比如我们刚定义的 say 函数。

这样的函数定义,在实际应用场景中常常会用到,举个例子,用户登录校验:

很显然,这里我们希望用户在执行 「login_user」前先检查一下用户名密码对不对,同样的,我们可以这样来拿到内部方法的引用:

那么通过这个 login ,我们就可以得到用户的校验了,当密码错误时:

当权限不足时:

只有用户名密码正确,才会执行 login_user 方法:

也许这时候你会发现,这种方式的使用,每次都需要像这样才能拿到引用去执行:

一点都不 Pythonic 呀,那么,@ 就派上用场了!!

可以直接这样使用,在 @ 后面添加一个你要进行额外操作的方法名称。

通过这样的定义,就等于 「login_new_user」拿到了 「user_check」的内部引用,可以直接执行:

你可以看到它们两的区别:

通过一个简单的 @ ,就可以让这个函数的拥有额外的功能,或者改变这个函数的执行结果。我们刚刚所说的「在 @ 后面添加一个你要进行额外操作的方法名称」,这个方法在 Python 中就叫 decorator——装饰器。

而这种语法,我们叫它「语法糖」。

语法糖(英语:Syntactic sugar)是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。

——来自维基百科

在装饰器中还有一种常用的方式,就是定义装饰器的同时,可以给它传递参数,拿刚刚的例子,假设我们除了用户名密码,还想校验密码的长度,但也许每个注册装饰器的方法,想要的长度不相同,那么可以这样:

在这个方法中,我们希望最小长度为 6 ,装饰器可以这样写:

这和我们一开始写的装饰器是差不多的,只不过为了接收变量参数,我们在定义了一个外层方法。另外,这里只是为了演示如何给装饰器传递参数,重点看 length 就行。

这样执行 user_login 就会得到这样的结果:

好了,现在的你已经知道了什么是装饰器,现在,回到一开始的问题。

这些玩意,知道是什么了吧?

它们就是 Python 已经写好了的装饰器,你的方法定义了什么装饰器,那它们就拥有额外的功能。

来给你简单介绍一下它们的功能。

通常,@property 和 @.setter 会搭配使用,比如上面的 name,通过 @name.setter 装饰,那么这个属性 name 的值就可以被改变,并且可以在方法中做一些简单的校验。

可以看到,通过 @property 装饰,这个方法行为可以直接被当作属性使用。所以有些方法可以直接使用 @property 来单纯返回一些数据,比如获取数据库中用户的记录有多少,就可以这样:

这时候只需要通过实例 user.user_count 就可以获取了。

而通过 @classmethod 装饰,你就可以把这个方法当作「工厂方法」,可以通过这个方法来创建实例:

通过 @staticmethod 装饰,这个方法就成为了「静态方法」,那么你可以直接通过类去访问它也可以通过实例去访问它:

这几个只是常见的装饰器,在一些项目中我们也常常会使用到装饰器,比如用 Python 写 web 项目,就有很多插件都是可以通过装饰器来简化代码,操作起来方便且可读性杠杠滴。

OK,这就是小帅b今天给你带来的分享。如果对你有帮助还请你分享,点赞或用你喜欢的方式支持我,我会给你带来更多内容。

8 Replies to “Python 爬阶梯 | 语法糖是什么玩意?Python中的函数高级用法——装饰器”

  1. superhandsomeB说道:

    返回return wrapper 这个我懂 但是为啥要返回这个呢? return func(username,password)

  2. tata说道:

    在两个装饰器的例子中,同时使用@func_1 @func_2,结果为什么不输出两次fxxk python?

    1. 小帅b说道:

      因为在这里调用第一个解释器 func_1 的时候,会执行到 func(*args, **kwargs) ,这时候并不是直接 return ,而是去调用 say ,而这时候的 say 会去调用第二个解释器,然后才 return。

  3. 登录验证的案例,
    else:
    return func(username, password) #不明白此处返回为什么一定要带参数,我调试过不带参数,运行为空

    1. 小帅b说道:

      因为这个时候验证通过,可以执行函数,而原有的函数里面是定义了需要携带用户名和密码这两个参数的,如果你不携带参数,那么应该是TypeError: login_user() missing 2 required positional arguments: ‘username’ and ‘password’ , 为啥会是空?

  4. angoe说道:

    say不需要参数的时候,这样做是否也是装饰器,即func_1中没有内嵌函数:
    def func_1(func):
    print(‘fuc_2’)
    func()

    @func_1
    def say():
    return(‘xiaoshuaib’)

    print(say)

    那是否可以理解为在say如果需要参数传入的情况下,就一定要像文中那样写一个嵌套函数?

    1. 小帅b说道:

      你可以再理解一下装饰器的概念,所谓装饰器就是接收另一个函数,然后扩展这个函数的行为而不是显示的直接修改它。

      在你写的例子中,func_1 函数没有返回任何东西,严格意义上来说,这就不算装饰器了。

      至于你说的是否一定需要嵌套函数,这个和是否需要传入参数无关,而是和业务逻辑有关,比如你要计时某一函数的执行时间,一般就会使用嵌套函数,而有些只需要执行一次的函数,也可以不使用嵌套。

回复 angoe 取消回复