说说在 Python 中如何实现输出指定函数运行时长的装饰器

假设我们需要一个可以输出某个函数运行时长的装饰器。

1 基础实现

一种可能的定义方式为:

这里利用函数装饰器,在 clock(func) 函数内部定义了一个 clock(*args) 函数,定义好后直接返回。内部利用 perf_count() 函数实现计算函数运行时长。每调用一次 perf_counter(),Python 就会记录一个时间点,类似于在秒表上按下开始计时键;当第二次调用该函数时,会计算与第一个时间点的时间长,类似于在秒表上按下结束计时键1

func.__name__ 会返回入参函数的名称。repr(arg) 会返回一个 arg的 string 格式2。通过一系列转换,我们就可以得到一个以逗号作为分隔符的入参字符串。

内部函数最后以这样的一种格式 [时长] 运行函数名(多个入参字符串) -> 输出结果 输出函数运行报告。其中的 %0.8fs 表示小数保留8位,然后再转换为字符串。

而外部函数最后返回这个 clocked(*args) 函数。

接着我们使用这个 clock 装饰器,来输出以下两个函数的运行报告:

  1. 睡眠函数;

  2. 斐波那契函数。

运行结果:

这里使用 @装饰函数名 这样的语法来包装我们需要运行的函数。

实际上等价于:

所以从写法上来讲,第一种方式更加简洁。

如果输出 logging.info('factorial.__name__ -> %s',factorial.__name__) 就会得到 factorial.__name__ -> clocked。这就说明了 factorial 实际上是 clocked 函数,也就是说factorial 函数已经被装饰为 clocked 函数。所以每次调用 factorial 函数,本质上就是调用 clocked 函数。

2 优化

前面说了,如果输出 logging.info('factorial.__name__ -> %s',factorial.__name__) 就会得到 factorial.__name__ -> clocked。也就是说,装饰函数 clock(func) 把 factorial(n) 函数给遮住了。如果我们不想被装饰函数的 __name__ 属性被遮住,可以这样做:

@functools.wraps 也是一个装饰器,它可以把 func 中的相关属性复制到 clocked 中。这样再次输出logging.info('factorial.__name__ -> %s',factorial.__name__) 就会得到 factorial.__name__ -> factorial 咯。


这实际上就是经典的装饰器设计模式,但在是实现方式上与普通的面向对象语言差别较大。普通的面向对象语言采用的是面向对象的编程方式,而 Python 采用的是面向函数的编程方式。

  1. Luciano Ramalho (作者),安道,吴珂 (译者).流畅的Python[M].人民邮电出版社,2017:319-322.

(0)

相关推荐

  • Python 中的函数装饰器和闭包

    函数装饰器可以被用于增强方法的某些行为,如果想自己实现装饰器,则必须了解闭包的概念. 装饰器的基本概念 装饰器是一个可调用对象,它的参数是另一个函数,称为被装饰函数.装饰器可以修改这个函数再将其返回, ...

  • 一文看懂Python系列之装饰器(decorator)(工作面试必读)

    Python的装饰器(decorator)可以说是Python的一个神器,它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能.Python的装饰器同时也是Python学习从入门到精通过程中 ...

  • python到底还有哪些函数我们还没见过?原来有这么多实用的函数

    本文将将主要介绍如下内容 函数介绍 1.1 函数的定义 所谓的函数,其实就是具有特定功能的一段代码块,都可以独立的运行 . 函数有5部分组成,分别是定义函数的关键字,函数的名字,圆括号(存放函数的参数 ...

  • 为什么 Python 没有函数重载?如何用装饰器实现函数重载?

    英文:https://arpitbhayani.me/blogs/function-overloading 作者:arprit 译者:豌豆花下猫("Python猫"公众号作者) 声 ...

  • Python学习—装饰器

    学习Python已经有一段时间了,陆续学了一些基础部分,但是理解的不是很深刻,每过一段时间就会忘记,所以不得不写一些博客进行记录,加深自己的理解.这两个星期一直在研究装饰器,开始觉得很简单,但是只知其 ...

  • Python中lambda用法和filter()函数

    "微信公众号" 目录 1. lambda用法. 2. filter()用法. 1. lambda用法. Python使用lambda来创建匿名函数. lambda只是一个表达式,函 ...

  • 说说Python中的help()和dir()函数?

    公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助! 小猿会从最基础的面试题开始, ...

  • 函数运行时在内存中是什么样子?

    在开始本篇的内容前,我们先来思考几个问题. 1. 我们先来看一段简单的代码: void func(int a) { if (a > 100000000) return;    int arr[1 ...

  • Python之pandas:对pandas中dataframe数据中的索引输出、修改、重命名等详细攻略

    Python之pandas:对pandas中dataframe数据中的索引输出.修改.重命名等详细攻略 对pandas中dataframe数据中的索引输出.修改.重命名等详细攻略 知识点学习 构造数据 ...

  • Python中三种格式化输出的方式

    程序中经常会出现这样的 场景:要求用户输入信息,然后打印成固定的格式 比如要求用户输入用户名和年龄,然后打印如下格式: # a = '------------- info -------------' ...

  • Python中tuple和list的区别?基础学习!

    想必大家都知道,Python数据类型有很多种,其中有两个对象的写法非常相似,它就是tuple元组和list列表,让人傻傻分不清楚.那么你知道Python中tuple和list有什么区别吗?我们来看看具 ...

  • Python中缩进是什么?入门分享!

    众所周知,Python是一门独特的编程语言,它语法清晰.简单易学,而且Python是通过缩进来识别代码块的,因为一般的语言都是通过{}或者end来作为代码块标记. Python中缩进是什么? 要求严格 ...