快捷搜索:  汽车  科技

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)在代码清单中,可以看到division()函数的定义。它只是简单返回除法结果。我们用两个参数(20 5)调用它,它返回4.0。但这里有一个问题。如果第二个数,也就是除数,是0会怎样呢?如果第二个参数为零,将得到一个ZeroDivisionError。我们可以在除法函数中实现一个try-except块来克服这个问题。但是我们不想修改函数体中的代码。所以,我们需要另一种方法。运算错误——division by Zero先来看一个简单的示例,假设我们想要定义一个函数 它需要两个数字 执行除法操作并返回结果。函数代码如下所示:运行上述程序,一个输出正确结果,一个报错:4.0

前言

在上一篇内容中,基于示例,介绍了Python装饰器的实现的背后逻辑,并给出了装饰器的一般实现模式和基本语法规范。详情请查阅:装饰器基本模式逻辑与实现。但是,前文中给出的示例很简单,也没有参数。在现实编程中,往往都需要参数支持的,以便支持更复杂的装饰目的。所以,这篇文章中就来看看如何实现带参装饰器以通用装饰器。

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(1)

提醒:请各位朋友点个赞,转发,收藏,和关注,以便随时回看,谢了先^_^

带参装饰器

通过前面的示例,主要理解了装饰器的机制和工作模式。但是其实现是简单的——没有任何参数。所以不要认为装饰器是没参数的。装饰器本质是函数,是函数就可以有参数,所以这节我们来看看有参数的装饰器。

现在我们看看如果想要装饰的函数接受一些参数会发生什么。我们将如何装饰这些功能?

先来看一个简单的示例,假设我们想要定义一个函数 它需要两个数字 执行除法操作并返回结果。函数代码如下所示:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(2)

运行上述程序,一个输出正确结果,一个报错:

4.0

运算错误——division by Zero

在代码清单中,可以看到division()函数的定义。它只是简单返回除法结果。我们用两个参数(20 5)调用它,它返回4.0。但这里有一个问题。如果第二个数,也就是除数,是0会怎样呢?如果第二个参数为零,将得到一个ZeroDivisionError。我们可以在除法函数中实现一个try-except块来克服这个问题。但是我们不想修改函数体中的代码。所以,我们需要另一种方法。

解决方案是用装饰器装饰这个函数。装饰器将负责检查第二个参数是否为零。我们来定义这个装饰器函数:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(3)

在上述代码中,我们定义了一个装饰器,其名称是divDecorator,它负责检查ZeroDivisionError。在包装器函数中,它检查b是否等于零。如果是,那么它只是打印一个错误消息并返回。如果b不为零,则调用函数f并返回它:return f(a b)。最后,divDecorator函数返回wrapper函数。请记住,这是Python中装饰器背后的思想。

现在,我们来用定义的装饰器函数来重新实现上述除法的调用——用divDecorator装饰division函数:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(4)

此时的已用装饰器装饰了division函数。我们改变前面的代码调用处理方式,如下所示:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(5)

运行程序所输出的结果类似如下:

4.0

除法的除数不能为0 !

None

正如输出结果中看到的,我们处理调了用者可能为除数传递一个0的情况,且没有修改原来division函数。这在装饰器的帮助下达到了这一目标 即divDecorator函数实现了这种情况下所需的所有逻辑。更重要的是要意识到,我们没有修改division函数,原函数没变,只是装饰了一下搞定了。

通用装饰器

在上一节中,我们看到了装饰器内部的division函数和wrapper函数的参数必须匹配。为什么?因为我们wrapper函数会取代装修后的division函数。所以它们的参数应该是匹配的。但这也带来了另一个问题。如果我们想对多个函数使用同一个装饰器,该怎么办呢?如果这些函数有不同数量的参数呢?现在让我们来回答这个问题。

首先,假设我们想要用大写字母打印用户的名字。有些用户可能只有名字,而另一些用户可能有姓和名。因此,我们将定义两个独立的函数为firstName和fullName。然后定义一个装饰器函数,由它来将名子转换为大写字母。装饰器如下:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(6)

在上述代码中,我们定义了一个通用装饰器。为什么它是通用的呢?

因为,在它的wrapper函数中,参数是*args。这使得wrapper函数可以接受任意数量的参数。换句话说,wrapper函数将能够出现在任何使用该装饰器装饰的函数上。

在包装器函数中,它修改了args元组中的项。它将每个项转换为大写后再将其添加到列表中。接着,在11行将这个列表转换为一个元组。最后,在第13行,它把*new_args元组作为参数传递给func调用,并返回:return func(*new_args)。

现在来完成对firstName和fullName函数的定义。我要这两个函数都用upperDecorator装饰器:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(7)

运行程序decExample04输出结果如下:

SOLO

SOLO CUI

在上述代码中,我们定义两个函数 firstName和fullName 并用上面所定义的装饰器来装饰。要小心了:这些函数有不同数量的参数。然后我们带参数来调用两个函数。在输出中 可看到这些名字都转换为大写了。这就是我们使用相同的生成器使用任意数量参数的方式。

这里有一个更通用的装饰器语法 包括*args和**kwargs,格式如下:

通俗理解python装饰器(装饰器之如何处理参数及通用装饰器实现)(8)

这两个参数含义和用法在介绍函数时介绍过,这里不再详细介绍。关键记住一条:一个是任意数量的元组性参数列表,一个表示任意键值的字典型参数列表。

小结

本篇内容主要介绍了装饰器的的更高级实现——带参数的装饰器的实现逻辑和处理、通用装饰器的实现及语法表现形式,以便编写更有普适性的装饰器。

本文就写到这里了。请朋友们点赞、转发和关注一下,再次感谢!

猜您喜欢: