您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Python

Python装饰器的细化讲解

时间:2023-02-14 12:23:47  来源:今日头条  作者:当年今时

我叫骆驼

会点儿代码,会点儿读书

这世上的书浩如烟海

我能做的就是尽量整理分享给你

在上一节Flask项目实战第一弹中我们讲到了路由,先看下面代码回顾一下:

from flask import Flask

App = Flask(__name__)


@app.route("/hello")
def hello():
    return "<p>Hello World ...</p>"


if __name__ == '__mAIn__':
    app.run(load_dotenv=True)

@app.route("/hello") 就是装饰器。交流群里小伙伴问我,Python/ target=_blank class=infotextkey>Python里的装饰器该怎么理解,今天我们好好唠唠这个东西。

说到装饰器,我们不得不谈一个知识点:闭包。我们从代码入手,一点一点来说闭包。

Python 有一个好玩的地儿,就是 def 函数(exterior)内部可以嵌套另一个 def 函数(interior)。调用 exterior 时,若遇到 interior , 仅仅完成对于 interior 的定义,而不去运行 interior。如果 exterior return interior,那么我们可以使用 interior () 去调用 内部函数 interior 函数。

 var = 0


def exterior():
    var = 1

    def interior():
        print(var)
    return interior()  # 这里返回 interior 函数调用结果


exterior() # 打印 1

从上面代码和结果中可以看到,interior 打印的 var 值 并非 第一行的 var。这说明,exterior 中的嵌套变量 var 覆盖了全局变量var=0,然后 interior 中的本地变量按照引用规则,就引用了var = 1。

接下来,我们仔细想想下面这句话:

interior 作用域在函数结束后就立即失效,而exterior嵌套作用域在 interior 的函数返回后却仍然有效。

var = 0


def exterior():
    var = 1

    def interior():
        print(var)
    return interior  # 这里返回 interior 函数对象


inter = exterior() 
inter()  # 打印 1

看完上面代码,再思考一下刚刚的话。如果还不清楚,看下图:

图解

创建一个闭包必须满足以下几点:

  • 必须有一个内嵌函数
  • 内嵌函数必须引用外部函数中的变量
  • 外部函数的返回值必须是内嵌函数

现在有了闭包的知识点,我们再聊聊装饰器(decorator)。我要掰开了揉碎了来说说装饰器。

刚刚接触装饰器的同学会对这个概念感到迷茫,然后你在网上(尤其 csdn)找例子或者教程,基本千篇一律,或者讲解的“点到为止”,你在看完之后,或许更迷茫了。

函数是什么

在说装饰器前,我们聊聊 Python 的函数。众所周知:在 Python 中,一切皆对象,函数是一等对象。

编程语言理论家把“一等对象”定义为满足下述条件的程序实体:

  • 在运行时创建①
  • 能赋值给变量或者数据结构中的元素②
  • 能作为参数传递给函数③
  • 能作为函数的返回结果④

我们看这么一段程序:

def double(x: int) -> int:
    return x * 2

这段代码很简单,计算了一个整数的2倍。那么我么用 dis 模块进行反编译,看看他是怎么运行的。

>>>from my_test import double
>>>from dis import dis

>>>dis(double)  # 结果如下

源码行号

指令在函数中的偏移

指令符号

指令参数

实际参数值

2

0

LOAD_FAST

0

x

 

2

LOAD_CONST

1

2

 

4

BINARY_MULTIRLY

 

 

 

6

RETURN_VALUE

 

 

指令符号解释:

  • LOAD_FAST :一般加载局部变量的值,也就是读取值,用于计算或者函数调用传参等;
  • LOAD_CONST :加载 const 变量,比如数值、字符串等等;
  • BINARY_MULTIRLY:见名知意,二进制乘法
  • RETURN_VALUE:返回值

结合反编译的结果,仔细理解一下代码的运行流程。下面我们看另外一个例子:

def double(x: int) -> int:
    return x * 2


def triple(x: int) -> int:
    return x * 3


def call_func(func, x: int) -> int:
    return func(x)


result = call_func(triple, 2)
print(result)
dis(call_func)

源码行号

指令在函数中的偏移

指令符号

指令参数

实际参数值

10

0

LOAD_FAST

0

func

 

2

LOAD_FAST

1

x

 

4

CALL_FUNCTION

1

 

 

6

RETURN_VALUE

 

 

在运行过程中:出现了 CALL_FUNCTION 。结合第13行代码,仔细体会一下这句话:函数能作为参数传递给另外一个函数。

我们现在看一下闭包的执行流程

def call_func():
    def double(x: int) -> int:
        return x * 2

    return double

dis(call_func)

里边出现了一个关键词:MAKE_FUNCTION,见名知意,创建函数。此时再回想“一等对象”所满足的条件。

说了这么多,无非是想告诉大家一个重要的东西,函数就是对象,可以被另一个函数返回,可以被赋值,也可以被调用。

其实到这里,才真是说完闭包这个东西。装饰器和闭包大同小异,下面我们接着来。

装饰器

有这种一种等价语法:

def callfunc(func):
    return 1


@callfunc
def triple(x: int) -> int:
    return x * 3

等价于

def callfunc(func):
    return 1


def triple(x: int) -> int:
    return x * 3


triple = callfunc(triple)

无论上面那种方式,我们输出的 tripre 这个对象的值都是 1

>>> print(triple)
>>> 1

所以,闭包可以写成@这种形式呢?其实,装饰器可以理解为闭包的一种,我们可以这样认为:闭包传递的是变量,而装饰器传递的是函数,除此之外没有任何区别。

我们看一个打印时间的装饰器:

import time


def timeit(func):
    def wrapper(x):
        start = time.time()
        ret = func(x)
        print(time.time() - start)
        return ret

    return wrapper


@timeit
def my_func(x):
    time.sleep(x)


my_func(1)

timeit 装饰器就打印 my_func 函数的运行时间。是不是在了解完闭包之后很简单了。

装饰器的作用就是:在不改变原函数的情况下,对已有函数进行额外的功能扩展。

恭喜你,Python 技能又进一步。

回到 Flask 我们看看路由装饰器

Flask 中路由的装饰器很简单,我们以 route 为例,以下是 route 函数源码(抽离版):

import typing as t


def add_url_rule(rule, endpoint, f, param):
    pass


def route(rule: str, **options: t.Any) -> t.Callable:

    def decorator(f: t.Callable) -> t.Callable:
        endpoint = options.pop("endpoint", None)
        add_url_rule(rule, endpoint, f, **options)
        return f

    return decorator

route 函数就是一个装饰器,内部嗲用 add_url_rule 实现真正的路由添加。再回过头看看装饰器的作用和定义以及使用,是不是明白了许多!加油,慢就快,快就是慢。



Tags:Python   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
一篇文章教会你使用Python中三种简单的函数
所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。一、函数简介所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。二、函数定义...【详细内容】
2024-04-11  Search: Python  点击:(7)  评论:(0)  加入收藏
一篇文章带你了解Python的分布式进程接口
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。一、前言在Thread和Process中,应当优...【详细内容】
2024-04-11  Search: Python  点击:(6)  评论:(0)  加入收藏
Python 可视化:Plotly 库使用基础
当使用 Plotly 进行数据可视化时,我们可以通过以下示例展示多种绘图方法,每个示例都会有详细的注释和说明。1.创建折线图import plotly.graph_objects as go# 示例1: 创建简单...【详细内容】
2024-04-01  Search: Python  点击:(10)  评论:(0)  加入收藏
Python 办公神器:教你使用 Python 批量制作 PPT
介绍本文将介绍如何使用openpyxl和pptx库来批量制作PPT奖状。本文假设你已经安装了python和这两个库。本文的场景是:一名基层人员,要给一次比赛活动获奖的500名选手制作奖状,并...【详细内容】
2024-03-26  Search: Python  点击:(21)  评论:(0)  加入收藏
Python实现工厂模式、抽象工厂,单例模式
工厂模式是一种常见的设计模式,它可以帮助我们创建对象的过程更加灵活和可扩展。在Python中,我们可以使用函数和类来实现工厂模式。一、Python中实现工厂模式工厂模式是一种常...【详细内容】
2024-03-07  Search: Python  点击:(35)  评论:(0)  加入收藏
不可不学的Python技巧:字典推导式使用全攻略
Python的字典推导式是一种优雅而强大的工具,用于创建字典(dict)。这种方法不仅代码更加简洁,而且执行效率高。无论你是Python新手还是有经验的开发者,掌握字典推导式都将是你技能...【详细内容】
2024-02-22  Search: Python  点击:(40)  评论:(0)  加入收藏
如何进行Python代码的代码重构和优化?
Python是一种高级编程语言,它具有简洁、易于理解和易于维护的特点。然而,代码重构和优化对于保持代码质量和性能至关重要。什么是代码重构?代码重构是指在不改变代码外部行为的...【详细内容】
2024-02-22  Search: Python  点击:(42)  评论:(0)  加入收藏
Python开发者必备的八个PyCharm插件
在编写代码的过程中,括号几乎无处不在,以至于有时我们会拼命辨别哪个闭合括号与哪个开头的括号相匹配。这款插件能帮助解决这个众所周知的问题。前言在PyCharm中浏览插件列表...【详细内容】
2024-01-26  Search: Python  点击:(91)  评论:(0)  加入收藏
Python的Graphlib库,再也不用手敲图结构了
Python中的graphlib库是一个功能强大且易于使用的工具。graphlib提供了许多功能,可以帮助您创建、操作和分析图形对象。本文将介绍graphlib库的主要用法,并提供一些示例代码和...【详细内容】
2024-01-26  Search: Python  点击:(90)  评论:(0)  加入收藏
大语言模型插件功能在携程的Python实践
作者简介成学,携程高级安全研发工程师,关注Python/Golang后端开发、大语言模型等领域。一、背景2023年初,科技圈最火爆的话题莫过于大语言模型了,它是一种全新的聊天机器人模型,...【详细内容】
2024-01-26  Search: Python  点击:(81)  评论:(0)  加入收藏
▌简易百科推荐
一篇文章教会你使用Python中三种简单的函数
所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。一、函数简介所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。二、函数定义...【详细内容】
2024-04-11  Go语言进阶学习  微信公众号  Tags:Python   点击:(7)  评论:(0)  加入收藏
一篇文章带你了解Python的分布式进程接口
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。一、前言在Thread和Process中,应当优...【详细内容】
2024-04-11  Go语言进阶学习    Tags:Python   点击:(6)  评论:(0)  加入收藏
Python 可视化:Plotly 库使用基础
当使用 Plotly 进行数据可视化时,我们可以通过以下示例展示多种绘图方法,每个示例都会有详细的注释和说明。1.创建折线图import plotly.graph_objects as go# 示例1: 创建简单...【详细内容】
2024-04-01  Python技术    Tags:Python   点击:(10)  评论:(0)  加入收藏
Python 办公神器:教你使用 Python 批量制作 PPT
介绍本文将介绍如何使用openpyxl和pptx库来批量制作PPT奖状。本文假设你已经安装了python和这两个库。本文的场景是:一名基层人员,要给一次比赛活动获奖的500名选手制作奖状,并...【详细内容】
2024-03-26  Python技术  微信公众号  Tags:Python   点击:(21)  评论:(0)  加入收藏
Python实现工厂模式、抽象工厂,单例模式
工厂模式是一种常见的设计模式,它可以帮助我们创建对象的过程更加灵活和可扩展。在Python中,我们可以使用函数和类来实现工厂模式。一、Python中实现工厂模式工厂模式是一种常...【详细内容】
2024-03-07  Python都知道  微信公众号  Tags:Python   点击:(35)  评论:(0)  加入收藏
不可不学的Python技巧:字典推导式使用全攻略
Python的字典推导式是一种优雅而强大的工具,用于创建字典(dict)。这种方法不仅代码更加简洁,而且执行效率高。无论你是Python新手还是有经验的开发者,掌握字典推导式都将是你技能...【详细内容】
2024-02-22  子午Python  微信公众号  Tags:Python技巧   点击:(40)  评论:(0)  加入收藏
如何进行Python代码的代码重构和优化?
Python是一种高级编程语言,它具有简洁、易于理解和易于维护的特点。然而,代码重构和优化对于保持代码质量和性能至关重要。什么是代码重构?代码重构是指在不改变代码外部行为的...【详细内容】
2024-02-22  编程技术汇    Tags:Python代码   点击:(42)  评论:(0)  加入收藏
Python开发者必备的八个PyCharm插件
在编写代码的过程中,括号几乎无处不在,以至于有时我们会拼命辨别哪个闭合括号与哪个开头的括号相匹配。这款插件能帮助解决这个众所周知的问题。前言在PyCharm中浏览插件列表...【详细内容】
2024-01-26  Python学研大本营  微信公众号  Tags:PyCharm插件   点击:(91)  评论:(0)  加入收藏
Python的Graphlib库,再也不用手敲图结构了
Python中的graphlib库是一个功能强大且易于使用的工具。graphlib提供了许多功能,可以帮助您创建、操作和分析图形对象。本文将介绍graphlib库的主要用法,并提供一些示例代码和...【详细内容】
2024-01-26  科学随想录  微信公众号  Tags:Graphlib库   点击:(90)  评论:(0)  加入收藏
Python分布式爬虫打造搜索引擎
简单分布式爬虫结构主从模式是指由一台主机作为控制节点负责所有运行网络爬虫的主机进行管理,爬虫只需要从控制节点那里接收任务,并把新生成任务提交给控制节点就可以了,在这个...【详细内容】
2024-01-25  大雷家吃饭    Tags:Python   点击:(61)  评论:(0)  加入收藏
站内最新
站内热门
站内头条