Python装饰器实例讲解(三)
Python装饰器实例讲解(三)
本文多参考《流畅的python》,在此基础上增加了一些实例便于理解
姊妹篇
Python装饰器实例讲解(一),让你简单的会用
Python装饰器实例讲解(二),主要讲了一个万能公式(原理)
本文其实反而是最最基础的部分,当然也回答了好几个关键的问题,也有一些是重复的地方
- 理解装饰器必须理解函数、闭包等概念
- 闭包后面单独讲,函数在本文是重点,从函数讲起
函数:一等对象
- 在Python中,函数是一等对象,需要满足以下条件:
- 在运行时创建
- 能赋值给变量或数据结构中的元素
- 能作为参数传给函数
- 能作为函数的返回结果
- 在Python中,整数、字符串和字典都是一等对象
函数名能赋值给变量
示例
def func():
print('hello') my_func = func # 此处不要写成func()
my_func() # hello
func() # hello
这样的使用比比皆是,比如在pytest中的一个应用
import pytest xfail = pytest.mark.xfail # 就是这里 @xfail # 这样看就比较简洁了
def test_hello():
assert 1 if __name__ == '__main__':
pytest.main(['-sv',__file__])
较为为典型的应用就是lambda,它是匿名的,但它同样可以赋值给一个变量
my_add = lambda x,y:x+y
result = my_add(1,2)
print(result) # 3
函数能作为参数传给函数
示例
def double(x):
return x*2 def triple(x):
return x*3 def calc(funcion_name,x):
return funcion_name(x) print(double(2)) # 4
print(triple(2)) # 6
print(calc(double,2)) # 4
print(calc(triple,2)) # 6
在上面的例子中你可以看到calc这个函数接收的第一个参数是函数名字
调用的时候你传入的是double、triple这样的名字
仔细观察代码,calc的实现其实的本意就是把第一个参数当做函数名,第二个参数是第一个参数的参数。所以本质上你可以做任何事情,只要这个函数仅接收一个参数即可
print(calc(bin,10)) # 返回的是bin(10)的结果 0b1010
print(calc(max,(2,5,3))) # 执行的是max((2,5,3))
高阶函数如map/filter/reduce/sort等,如果你接触过,他们的参数不都是函数名吗?
我也写过一篇文章,Python函数式编程之map/filter/reduce/sorted
能作为函数的返回结果
示例
def add(x,y):
return x+y def func():
print('calling func')
return add print(func()(1,2))
# 输出如下
# calling func
# 3
# func() 就是 add , 跟你执行add(1,2)的效果是一样的
你也可以这样
new_add = func()
print(new_add(1,2))
# calling func
# 3
如果你看过前面的两篇文章,到这里就应该很熟悉了
可调用对象
除了函数是可调用的,还有很多(其实也没多少)都是可调用对象
按照流畅的python的说法,有这么多可调用对象
可调用对象 说明 用户定义的函数 使用 def 语句或 lambda 表达式创建 内置函数 使用 C 语言(CPython)实现的函数,如 len 或 time.strftime 内置方法 使用 C 语言实现的方法,如 dict.get 方法 在类的定义体中定义的函数 类 调用类时会运行类的 new 方法创建一个实例,然后运行 init 方法,初始化实 例,最后把实例返回给调用方。因为 Python 没有 new 运算符,所以调用类相当于调用函数。 类的实例 如果类定义了 call 方法,那么它的实例可以作为函数调用。 生成器函数 使用 yield 关键字的函数或方法。调用生成器函数返回的是生成器对象。
对普通的初学者而言其实就是函数和类,类的调用分2级,Obj()这是实例化,同时调用new和init。
new和init魔术方法,后面会单独开篇讲解,单例跟这个是息息相关的。
生成器后面也考虑单独开文章说一下。
示例代码(说明new和init)
class Person:
def __new__(cls, *args, **kwargs):
print('calling new')
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self):
print('calling init') wuxianfeng = Person()
示例输出
calling new
calling init
但此时wuxianfeng这个Person类的实例并不是可调用的对象
如果你写wuxianfeng(),会给你提示
TypeError: 'Person' object is not callable
你需要在Person类中定义一个__call__方法
class Person:
...
def __call__(self, *args, **kwargs):
print('callable')
此时再次执行wuxianfeng()就可以得到callable了
当然如果你执行Person()()结果也是这样的
calling new
calling init
callable
python提供了一个内置的callable()函数来检测对象是否可调用
print([callable(obj) for obj in (abs, str, 13)]) # [True, True, False]
回到装饰器
虽然你可能已经学到装饰器三了,但请你清空下你了解的装饰器,倒也不是从0开始,带点复习
示例代码
def decorate(function_name):
def inner():
print('calling inner')
function_name()
return inner
@decorate
def target():
print('calling target') target()
输出结果
calling inner
calling target
根据万能公式,分析下执行过程
当你在执行target()的时候,由于target上有个装饰器,实际上发生的事情是target = decorate(target)
前面的target 是新的(一个变量),后面的decorate(target)中的target是你之前定义的函数
decorate(target)就会去调用decorate函数传入target参数,返回inner
卡....返回了inner,是你加了装饰器的效果,至此都没有执行函数
正是由于最终的target(),就是去调用了inner(),对应的语句是
print('calling inner')
function_name() # 你传入的是target就是此处的function_name
- 说一些理论
- 装饰器只是语法糖
- 装饰器可以像常规的可调用对象那样调用,其参数是另一个函数(被装饰的函数)。
- 装饰器可能会处理被装 饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
- 装饰器的一大特性是,能把被装饰的函数替换成其他函数
- 第二个特性是,装饰器在加载模块时立即执行
关于被替换
def decorate(function_name):
def inner():
print('calling inner')
function_name()
print('这是inner的id:',id(inner))
return inner
@decorate
def target():
print('calling target') print('这是target的id:',id(target))示例输出(你输出的id跟我肯定不一样,但2者应该是一致的,从这个角度也能看出来你执行的target不再是原来的target了)
这是inner的id: 1804087435904
这是target的id: 1804087435904
叠放装饰器
日常代码中还是有一些场景能看到一个函数被多个装饰器装饰的情况,比如pytest的allure
这个执行顺序就是如你所想的那般,先装饰的先执行
示例代码
def decorate1(function_name):
def inner1():
print('calling inner1')
function_name()
return inner1 def decorate2(function_name):
def inner2():
print('calling inner2')
function_name()
return inner2 @decorate1
@decorate2
def target():
print('calling target') target()
# 输出
# calling inner1
# calling inner2
# calling target
但这种情况下的万能公式是怎样的呢???你知道不~
万能公式1
@decorate1
def target():
print('calling target') # 等价于做了一件事
target = decorate1(target)
万能公式2
@decorate1
@decorate2
def target():
print('calling target') # 等价于做了2件事
# 第一件事,注意,就近原则
target = decorate2(target) # 前面的target是新的变量,后面的target是def的最初的、原始的函数
# 第二件事
target = decorate1(target) # 前面的target又是一个新的变量,后面的target是line8的前面的target # 你也可以理解为做了一件事(合并上面2行)
target = decorate1(decorate2(target) ) # 最近的@的先调用
不信请看
def decorate1(function_name):
def inner1():
print('calling inner1')
function_name()
return inner1 def decorate2(function_name):
def inner2():
print('calling inner2')
function_name()
return inner2 def target():
print('calling target') target = decorate2(decorate1(target) )
target()
- 装饰器就讲到这里了
- 会用是第一步,理解简单的过程是第二步,会写一个装饰器才算是基本懂了
Python装饰器实例讲解(三)的更多相关文章
- Python装饰器实例讲解(二)
Python装饰器实例讲解(二) Python装饰器实例讲解(一) 你最好去看下第一篇,虽然也不是紧密的链接在一起 参考B站码农高天的视频,大家喜欢看视频可以跳转忽略本文:https://www.bi ...
- Python装饰器实例讲解(一)
Python装饰器实例讲解(一) 多种角度讲述这个知识,这是个系列文章 但前后未必有一定的顺承关系 部分参考网络 本文以一个小案例引出装饰器的一些特点,不涉及理论,后面再谈 案例 写一个代码来求一个数 ...
- python --装饰器内容讲解
python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能. 3.1 定义装饰器 ...
- python 装饰器(六):装饰器实例(三)内置装饰器
内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象,所以更难理解一些. @property 在了解这个装饰器前,你需要知道在不使用装饰器怎么写一个属性. def getx(sel ...
- python --装饰器通俗讲解
装饰器 什么是装饰器?:在不修改源代码和调用方式的基础上给其增加新的功能,多个装饰器可以装饰在同一个函数上 Python中的装饰器是你进入Python大门的一道坎; 装饰器特点: 不改变原函数原代码: ...
- Python 装饰器实例
retry 偶然看到一篇文章,想到了前几天的一个需求,git pull性能不稳,需要加入重试机制,正好这个装饰器的实例符合这样的场景. # coding:utf-8 import time impor ...
- Python装饰器与面向切面编程
今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...
- python设计模式之装饰器详解(三)
python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...
- Python的装饰器实例用法小结
这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...
- Python装饰器讲解
Python装饰器讲解 定义:本质是函数,就是为其他函数添加附加功能.原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 import time def timmer(func ...
随机推荐
- 【Java并发006】使用层面:Lock锁机制全解析
一.前言 二.synchronized局限性 + Lock锁机制的引入 2.1 synchronized局限性 第一,使用synchronized,其他线程只能等待直到持有锁的线程执行完释放锁(syn ...
- 【Devexpres】spreadsheetControl设置可见范围
// 获得当前电子表格的工作簿 Worksheet worksheet = spreadsheetControl.ActiveWorksheet; // 获得当前用户数据范围 CellRange us ...
- 关于led蓝牙控制器ble通信分析
前言 前几天在网上买了一个led蓝牙控制器,可以用手机app通过蓝牙连接控制rgb led灯,当然这个也是属于ble通信.之前我写过一篇体重称蓝牙通信的,不过那个较为简单,数据也是靠分析出来的. 这次 ...
- AI绘画提示词创作指南:DALL·E 2、Midjourney和 Stable Diffusion最全大比拼 ⛵
作者:韩信子@ShowMeAI 深度学习实战系列:https://www.showmeai.tech/tutorials/42 自然语言处理实战系列:https://www.showmeai.tech ...
- shell编写循环检查脚本
背景:如下脚本实现当微服务重启后,检查微服务的启动端口正常,可通过轮询的方式来实现所需要用到配置文件config.properties信息如下: onlineService:8001 algorthS ...
- MetaTown:一个可以自己构建数字资产的平台
摘要:华为云Solution as Code重磅推出<基于MetaTown构建数字资产平台>解决方案. 本文分享自华为云社区<基于MetaTown构建数字资产平台>,作者: 阿 ...
- 跨机房ES同步实战
作者:谢泽华 背景 众所周知单个机房在出现不可抗拒的问题(如断电.断网等因素)时,会导致无法正常提供服务,会对业务造成潜在的损失.所以在协同办公领域,一种可以基于同城或异地多活机制的高可用设计,在保障 ...
- 关于JavaScript每句结尾是否需要添加分号问题
最近在学习JS的时候遇到这么一个问题.由于我之前的学习中一直是写一句JS代码,加一个分号.但是最近我才发现原来JS代码是可以不添加分号的.如果可以不写分号的话会不会更省事呢?于是我在网上查了相关资料整 ...
- 网络基础与osi七层与TCP/IP协议
一 什么是网络 网络:计算机网络是一组计算机或网络设备通过有形 的线缆或无形的媒介如无线,连接起来,按照一定的 规则,进行通信的集合. 通信,是指人与人.人与物.物与物之间通过某种媒 介和行为进行的 ...
- 《HTTP权威指南》– 4.HTTP连接管理
浏览器解析URL流程: 浏览器解析出域名: 浏览器查询这个主机名的IP地址: 浏览器获得端口号: 浏览器发起到主机名IP地址端口的80连接: 浏览器向服务器发送一条HTTP–GET报文: 浏览器从服务 ...