Python|Python装饰器以及高级用法


Python|Python装饰器以及高级用法文章插图
介绍
首先我要承认 , 装饰器非常难!你在本教程中看到的一些代码将会有一些复杂 。 大多数人在学习Python时都跟装饰器做过斗争 , 所以如果这对你来说很奇怪 , 不要感到沮丧 , 因为同样的大多数人都可以克服这种苦难 。 在本教程中 , 我将逐步介绍了解装饰器的过程 。 首先我假设你已经可以编写基本函数和基本类 。 如果你不能做这些事 , 那么我建议你在回到这里之前先学习如何去做到编写基本函数和基本类(除非你迷路了 , 在这种情况下你可以原谅) 。
用例:计时函数执行
假设我们正在执行一段代码 , 执行时间比我们想的还要长一些 。 这段代码由一堆函数调用组成 , 我们确信这些调用中至少有一个调用构成了我们代码中的瓶颈 。 我们如何找到瓶颈?现在有一个解决方案 , 就是我们现在要关注的解决方案 , 就是对函数执行进行计时 。
让我们从一个简单的例子开始 。 我们只有一个函数需要计时 , func_a
def func_a(stuff): do_important_things_1() do_important_things_2() do_important_things_3()一种方法是将时钟代码放在每个函数调用周围 。 所以就像这样:
func_a(current_stuff)看起来会更像这样:
before = datetime.datetime.now()func_a(current_stuff)after = datetime.datetime.now()print ("Elapsed Time = {0}".format(after-before))这样就可以了 。 但是如果我们有多次调用func_a并且我们想要为所有这些计时会发生什么呢?我们可以用计时代码包围func_a的每个调用 , 但是这样做也有不好的效果 。 它只准备编写一次计时代码 。 因此 , 我们将其放在函数定义中 , 而不是将其放在函数之外 。
def func_a(stuff): before = datetime.datetime.now() do_important_things_1() do_important_things_2() do_important_things_3() after = datetime.datetime.now() print("Elapsed Time = {0}".format(after-before))这种方法的好处是:

  1. 我们将代码放在一个地方 , 所以如果我们想要更改它(例如 , 如果我们想将经过的时间存储在数据库或日志中)那么我们只需要在一个地方而不是每一个函数调用中更改它
  2. 我们不需要记住每次调用func_a都要写四行代码而不是一行 , 这是非常好的
好的 , 但是只需要计算一个函数的时间是不现实的 。 如果你需要对一件事进行计时 , 你很有可能需要至少对两件事进行计时 。 所以我们会选择三个 。
def func_a(stuff): before = datetime.datetime.now() do_important_things_1() do_important_things_2() do_important_things_3() after = datetime.datetime.now() print("Elapsed Time = {0}".format(after-before))def func_b(stuff): before = datetime.datetime.now() do_important_things_4() do_important_things_5() do_important_things_6() after = datetime.datetime.now() print("Elapsed Time = {0}".format(after-before))def func_c(stuff): before = datetime.datetime.now() do_important_things_7() do_important_things_8() do_important_things_9() after = datetime.datetime.now() print("Elapsed Time = {0}".format(after-before))这看起来很糟糕 。 如果我们想要对8个函数进行计时的时候怎么办?然后我们决定将计时的信息存储在日志文件中 。 然后我们决定建立一个更好的数据库 。 我们这里需要的是将一种相同的代码合并到func_a , func_b和func_c中的方法 , 这种方法不会让我们到处复制粘贴代码 。
一个简单的绕道:返回函数的函数
Python是一种非常特殊的语言 , 因为函数是第一类对象 。 这意味着一旦函数在作用域中被定义 , 它就可以传递给函数 , 赋值给变量 , 甚至从函数返回 。 这个简单的事实是使python装饰器成为可能的原因 。 查看下面的代码 , 看看你是否可以猜出标记为A , B , C和D的行会发生什么 。
def get_function(): print ("inside get_function")def returned_function():print("inside returned_function")return 1 print("outside returned_function") return returned_functionreturned_function() # A x = get_function() # B x # C x() # DA
这一行给出了一个NameError并声明returned_function不存在 。 但我们只是定义了它 , 对吧?你在这里需要知道的是 , 它是在get_function的范围内定义的 。 也就是说 , 在get_function里面定义了它 。 它不是在get_function之外 。 如果这让你感到困惑 , 那么你可以尝试使用该locals()函数 , 并阅读Python的范围 。
B
这行代码打印出以下内容:
inside get_functionoutside returned_function此时Python不执行returned_function的任何内容 。
C
这一行输出:
也就是说 , get_function()返回的值x本身就是一个函数 。