python装饰器Decorators

本人在www.programiz.com网站上搜到一篇讲解python装饰器的文章,由浅入深,步步深入,可以一读。

声明:此篇文章是从programiz.com网站摘录得到的,并非本人原创。原来为英文版本,本人翻译成中文版本,并加了部分批注,如有错误还请指正。

 

Python Decorators

A decorator takes in a function, adds some functionality and returns it. In this article, you will learn how you can create a decorator and why you should use it.

What are decorators in Python?

Python has an interesting feature called decorators to add functionality to an existing code.

This is also called metaprogramming as a part of the program tries to modify another part of the program at compile time.

Python装饰器

装饰器接受一个函数,添加一些功能并返回它。 在本文中,您将学习如何创建装饰器以及为什么要使用它。

Python中的装饰器是什么?

Python有一个有趣的功能,称为装饰器,可将功能添加到现有代码中。

这也称为元编程,因为程序的一部分试图在编译时修改程序的另一部分。

 

Prerequisites for learning decorators

In order to understand about decorators, we must first know a few basic things in Python.

We must be comfortable with the fact that, everything in Python (Yes! Even classes), are objects. Names that we define are simply identifiers bound to these objects.

Functions are no exceptions, they are objects too (with attributes).

Various different names can be bound to the same function object.

Here is an example.

学习装饰的先决条件

为了了解装饰器,我们必须首先了解Python的一些基本知识。

我们必须对以下事实感到满意:Python中的所有内容(是的!甚至是类)都是对象。 我们定义的名称只是绑定到这些对象的标识符。

函数也不例外,它们也是对象(带有属性)。

可以将各种不同的名称绑定到同一功能对象。

这是一个例子。

 

1 def first(msg):
2     print(msg)    
3 
4 first("Hello")
5 
6 second = first
7 second("Hello")

 

 

When you run the code, both functions first and second gives same output.

Here, the names first and second refer to the same function object.

Now things start getting weirder.

Functions can be passed as arguments to another function.

If you have used functions like map, filter and reduce in Python, then you already know about this.

Such function that take other functions as arguments are also called higher order functions.

Here is an example of such a function.

当您运行代码时,第一个和第二个函数将提供相同的输出。

在此,名称first和second指的是同一功能对象。

现在事情开始变得怪异了。

可以将函数作为参数传递给另一个函数。

如果您在Python中使用过map,filter和reduce等功能,那么您已经知道了。

这种将其他函数作为参数的函数也称为高阶函数

这是这种功能的一个例子。

1 def inc(x):
2     return x + 1
3 
4 def dec(x):
5     return x - 1
6 
7 def operate(func, x):
8     result = func(x)
9     return result

We invoke the function as follows.

我们调用函数如下。

1 >>> operate(inc,3)
2 4
3 >>> operate(dec,3)
4 2

 

 

Furthermore, a function can return another function.

此外,一个函数可以返回另一个函数。

1 def is_called():
2     def is_returned():
3         print("Hello")
4     return is_returned
5 
6 new = is_called()
7 
8 #Outputs "Hello"
9 new()

 

 

PS:上面这段代码是啥意思?刚开始的时候没看明白,请继续往下看

Here, is_returned() is a nested function which is defined and returned, each time we call is_called().

 

Finally, we must know about closures in Python.

在这里,is_returned()是一个嵌套函数,每次我们调用is_called()时,该函数都会定义并返回。

PS:is_called()是一个嵌套函数,

调用is_called()时,就会执行下面红色方框中的两句话,怎么理解这这句话呢?

就好比把这两句同时向左缩进4个空格,即就像def is_called():这句一样,系统定义了这个函数,

那么在这里,每次调用一次is_called()函数,即调用is_called()函数并获取其返回值,

就相当于定义了一次is_returned()函数,

最后呢,执行return is_returned这句话,就是返回is_returned()函数的函数名,就像上面那个second的名字和first的名字代表的实际函数是一致的。

一句话总结:就是在is_called()函数内部定义了一个函数,定义完之后,返回的是内部函数的函数名字。

 

最后,我们必须了解Python中的闭包。

Getting back to Decorators

Functions and methods are called callable as they can be called.

In fact, any object which implements the special method __call__() is termed callable. So, in the most basic sense, a decorator is a callable that returns a callable.

Basically, a decorator takes in a function, adds some functionality and returns it.

回到装饰器

函数和方法被称为可调用的,因为它们可以被调用。

实际上,任何实现特殊方法__call __()的对象都称为可调用的。

因此,从最基本的意义上讲,装饰器是可调用的,可返回可调用的。

基本上,装饰器接受一个函数,添加一些功能并返回它。

 

1 def make_pretty(func):
2     def inner():
3         print("I got decorated")
4         func()
5     return inner
6 
7 def ordinary():
8     print("I am ordinary")

 

 

When you run the following codes in shell,

在shell中运行以下代码时,

1 >>> ordinary()
2 I am ordinary
3 
4 >>> # let's decorate this ordinary function
5 >>> pretty = make_pretty(ordinary)
6 >>> pretty()
7 I got decorated
8 I am ordinary

 

In the example shown above, make_pretty() is a decorator.

In the assignment step.

在上面显示的示例中,make_pretty()是装饰器。

在分配步骤中。

1 pretty = make_pretty(ordinary)

 

The function ordinary() got decorated and the returned function was given the name pretty.

We can see that the decorator function added some new functionality to the original function.

This is similar to packing a gift.

 The decorator acts as a wrapper.

The nature of the object that got decorated (actual gift inside) does not alter.

But now, it looks pretty (since it got decorated).

Generally, we decorate a function and reassign it as,

函数ordinary()被装饰了,并将返回的函数命名为pretty

我们可以看到装饰器函数在原始函数中添加了一些新功能。

 这类似于包装礼物。

 装饰器充当包装器。

 装饰的对象(内部实际礼物)的性质不会改变。

但是现在,它看起来很漂亮(因为它已经被装饰了)。

通常,我们装饰一个函数并将其重新分配为

1 ordinary = make_pretty(ordinary)

 

PS:等号右边返回的是make_pretty()内部函数的函数名字,

再将右边的返回值(内部函数名字)赋值给ordinary变量。

 

由于make_pretty()是一个decorator,返回一个函数,所以,原来的ordinary()函数仍然存在,只是现在同名的ordinary变量指向了新的函数,于是调用ordinary()将执行新函数,即在ordinary()函数中返回的inner()函数。

 

This is a common construct and for this reason, Python has a syntax to simplify this.

We can use the @ symbol along with the name of the decorator function and place it above the definition of the function to be decorated.

For example,

这是一个常见的构造,因此,Python具有简化此语法的语法。

我们可以将@符号与装饰器函数的名称一起使用,并将其放置在要装饰的函数的定义上方。

例如,

 

1 @make_pretty
2 def ordinary():
3     print("I am ordinary")

 

 

is equivalent to

相当于

1 def ordinary():
2     print("I am ordinary")
3 ordinary = make_pretty(ordinary)

 

This is just a syntactic sugar to implement decorators.

这只是实现装饰器的语法糖。

 

Decorating Functions with Parameters

The above decorator was simple and it only worked with functions that did not have any parameters. What if we had functions that took in parameters like below?

用参数装饰函数

上面的装饰器很简单,并且只适用于没有任何参数的函数。

如果我们的函数具有如下所示的参数,该怎么办?

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

 

This function has two parameters, a and b.

We know, it will give error if we pass in b as 0.

此函数有两个参数,a和b。

我们知道,如果我们将b传递为0,则会产生错误。

 

1 >>> divide(2,5)
2 0.4
3 >>> divide(2,0)
4 Traceback (most recent call last):
5 ...
6 ZeroDivisionError: division by zero

 

Now let's make a decorator to check for this case that will cause the error.

现在让我们做一个装饰器来检查这种情况是否会导致错误。

 1 def smart_divide(func):
 2    def inner(a,b):
 3       print("I am going to divide",a,"and",b)
 4       if b == 0:
 5          print("Whoops! cannot divide")
 6          return
 7 
 8       return func(a,b)
 9    return inner
10 
11 @smart_divide
12 def divide(a,b):
13     return a/b

 

This new implementation will return None if the error condition arises.

如果出现错误情况,此新实现将返回None。

1 >>> divide(2,5)
2 I am going to divide 2 and 5
3 0.4
4 
5 >>> divide(2,0)
6 I am going to divide 2 and 0
7 Whoops! cannot divide

 

PS:总结一下,装饰器返回的是内部函数的函数名,实际运行的是内部函数的调用,

内部函数包含了要装饰的函数,被装饰的函数是内部的函数的一部分。

 

In this manner we can decorate functions that take parameters.

A keen observer will notice that parameters of the nested inner() function inside the decorator is same as the parameters of functions it decorates.

Taking this into account, now we can make general decorators that work with any number of parameter.

In Python, this magic is done as function(*args, **kwargs).

In this way, args will be the tuple of positional arguments and kwargs will be the dictionary of keyword arguments.

An example of such decorator will be.

通过这种方式,我们可以装饰带有参数的函数。

敏锐的观察者会注意到,装饰器内部嵌套的inner()函数的参数与其装饰的函数的参数相同。

考虑到这一点,现在我们可以使通用装饰器可以使用任意数量的参数。

在Python中,这种魔术是通过function(* args,** kwargs)完成的。

这样,args将是位置参数的元组,而kwargs将是关键字参数的字典。

这样的装饰器的一个例子是。

1 def works_for_all(func):
2     def inner(*args, **kwargs):
3         print("I can decorate any function")
4         return func(*args, **kwargs)
5     return inner

 

Chaining Decorators in Python

Multiple decorators can be chained in Python.

This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.

Python中的链接装饰器

可以在Python中链接多个装饰器。

这就是说,一个函数可以用不同(或相同)的装饰器多次装饰。 我们只需将装饰器放置在所需功能之上。

 1 def star(func):
 2     def inner(*args, **kwargs):
 3         print("*" * 30)
 4         func(*args, **kwargs)
 5         print("*" * 30)
 6     return inner
 7 
 8 def percent(func):
 9     def inner(*args, **kwargs):
10         print("%" * 30)
11         func(*args, **kwargs)
12         print("%" * 30)
13     return inner
14 
15 @star
16 @percent
17 def printer(msg):
18     print(msg)
19 printer("Hello")

 

This will give the output.

这将给出输出。

1 ******************************
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Hello
4 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 ******************************

 

PS:为什么是这样的输出呢?请往下看

The above syntax of,

上面的语法,

1 @star
2 @percent
3 def printer(msg):
4     print(msg)

is equivalent to

相当于

1 def printer(msg):
2     print(msg)
3 printer = star(percent(printer))

 

The order in which we chain decorators matter.

If we had reversed the order as,

链接装饰器的顺序很重要。

如果我们按相反的顺序,

1 @percent
2 @star
3 def printer(msg):
4     print(msg)

 

The execution would take place as,

执行将发生的,

1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 ******************************
3 Hello
4 ******************************
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

相关链接:

1. https://www.programiz.com/python-programming/decorator

2. https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584

posted @ 2020-04-22 23:59  xyu1  阅读(412)  评论(0编辑  收藏  举报