Python也可以拥有延迟函数
延迟函数defer
我们知道在Golang中有一个关键字defer
,用它来声明在函数调用前,会让函数*延迟**到外部函数退出时再执行,注意,这里的退出含义:函数return返回或者函数panic退出
defer的特性
defer
函数会在外层函数执行结束后执行
package main
import "fmt"
func main() {
defer fmt.Println(2)
fmt.Println(1)
}
/* output:
1
2
*/
defer
函数会在外层函数异常退出时执行
package main
import "fmt"
func main() {
defer fmt.Println(2)
panic(1)
}
/* output:
2
panic: 1
goroutine 1 [running]:
main.main()
/tmp/sandbox740231192/prog.go:7 +0x95
*/
3.如果函数中有多个defer
函数,它们的执行顺序是LIFO:
package main
import "fmt"
func main() {
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println(1)
}
/*output:
1
3
2
*/
defer的用途
释放资源
比如打开文件后关闭文件:
package main
import "os"
func main() {
file, err := os.Open("1.txt")
if err != nil {
return
}
// 关闭文件
defer file.Close()
// Do something...
}
又比如数据库操作操作后取消连接
func createPost(db *gorm.DB) error {
conn := db.Conn()
defer db.Close()
err := conn.Create(&Post{Author: "Draveness"}).Error
return err
}
recover恢复
package main
import "fmt"
func main() {
defer func() {
if ok := recover(); ok != nil {
fmt.Println("recover")
}
}()
panic("error")
}
/*output:
recover
*/
总结
defer
函数总会被执行,无论外层函数是正常退出还是异常panic- 如果函数中有多个
defer
函数,它们的执行顺序是LIFO
在Python中的写一个defer
看到defer
这么好用,Pythoneer也可以拥有吗?当然
家里(Python)的条件
Python中有一个库叫做contextlib
,它有一个类叫ExitStack
,来看一下官网的介绍:
A context manager that is designed to make it easy to programmatically combine other context managers and cleanup functions, especially those that are optional or otherwise driven by input data.
Since registered callbacks are invoked in the reverse order of registration, this ends up behaving as if multiple nested with statements had been used with the registered set of callbacks. This even extends to exception handling - if an inner callback suppresses or replaces an exception, then outer callbacks will be passed arguments based on that updated state.
Since registered callbacks are invoked in the reverse order of registration 这句是关键,他说注册的回调函数是以注册顺序相反的顺序被调用,这不就是defer
函数的第二个特性LIFO吗?
再看下ExitStack
类:
class ExitStack(ContextManager[ExitStack]):
def __init__(self) -> None: ...
def enter_context(self, cm: ContextManager[_T]) -> _T: ...
def push(self, exit: _CM_EF) -> _CM_EF: ...
def callback(self, callback: Callable[..., Any], *args: Any, **kwds: Any) -> Callable[..., Any]: ...
def pop_all(self: _U) -> _U: ...
def close(self) -> None: ...
def __enter__(self: _U) -> _U: ...
def __exit__(
self,
__exc_type: Optional[Type[BaseException]],
__exc_value: Optional[BaseException],
__traceback: Optional[TracebackType],
) -> bool: ...
可以看到它实现了是上下文管理器的协议__enter__
和__exit__
,所以是可以保证defer
的第一个特性:defer
函数总会被执行
让我们来测试一下
import contextlib
with contextlib.ExitStack() as stack:
stack.callback(lambda: print(1))
stack.callback(lambda: print(2))
print("hello world")
raise Exception()
输出:
hello world
2
1
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
/defer_test.py in <module>
6
7 print("hello world")
----> 8 raise Exception()
Exception:
nice! 这行的通
行动
让我们做一下封装,让它更通用一些吧
类版本,像defer
那样
import contextlib
class Defer:
def __init__(self, *callback):
"""callback is lambda function
"""
self.stack = contextlib.ExitStack()
for c in callback:
self.stack.callback(c)
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
self.stack.__exit__(exc_type, exc_val, exc_tb)
if __name__ == "__main__":
with Defer(lambda: print("close file"), lambda: print("close conn")) as d:
print("hello world")
raise Exception()
输出:
hello world
close conn
close file
Traceback (most recent call last):
File "defer.py", line 38, in <module>
raise Exception()
Exception
通过配合lambda
表达式,我们可以更加灵活
装饰器版本,不侵入函数的选择
import contextlib
def defer(*callbacks):
def decorator(func):
def wrapper(*args, **kwargs):
with contextlib.ExitStack() as stack:
for callback in callbacks:
stack.callback(callback)
return func(*args, **kwargs)
return wrapper
return decorator
@defer(lambda: print("logging"), lambda: print("close conn..."))
def query_exception(db):
print("query...")
raise Exception()
if __name__ == "__main__":
db = None
query_exception(db)
输出:
query...
close conn...
logging
Traceback (most recent call last):
File "defer.py", line 43, in <module>
query_exception(db)
File "defer.py", line 25, in wrapper
return func(*args, **kwargs)
File "defer.py", line 38, in query_exception
raise Exception()
Exception
Get!快学起来吧~
Python也可以拥有延迟函数的更多相关文章
- Python的函数式编程-传入函数、排序算法、函数作为返回值、匿名函数、偏函数、装饰器
函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...
- Python的常用内置函数介绍
Python的常用内置函数介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.取绝对值(abs) #!/usr/bin/env python #_*_coding:utf-8_ ...
- python函数式编程之返回函数、匿名函数、装饰器、偏函数学习
python函数式编程之返回函数 高阶函数处理可以接受函数作为参数外,还可以把函数作为结果值返回. 函数作为返回值 def laxy_sum(*args): def sum(): ax = 0; fo ...
- Python学习笔记之常用函数及说明
Python学习笔记之常用函数及说明 俗话说"好记性不如烂笔头",老祖宗们几千年总结出来的东西还是有些道理的,所以,常用的东西也要记下来,不记不知道,一记吓一跳,乖乖,函数咋这么多 ...
- [python学习] 语言基础—排序函数(sort()、sorted()、argsort()函数)
python的内建排序函数有 sort.sorted两个. 1.基础的序列升序排序直接调用sorted()方法即可 ls = list([5, 2, 3, 1, 4]) new_ls = sorted ...
- python 装饰器修改调整函数参数
简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...
- Python 函数式编程 & Python中的高阶函数map reduce filter 和sorted
1. 函数式编程 1)概念 函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念.wiki 我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数 ...
- Python中的高阶函数与匿名函数
Python中的高阶函数与匿名函数 高阶函数 高阶函数就是把函数当做参数传递的一种函数.其与C#中的委托有点相似,个人认为. def add(x,y,f): return f( x)+ f( y) p ...
- Delphi 延迟函数 比sleep 要好的多
转自:http://www.cnblogs.com/Bung/archive/2011/05/17/2048867.html //延迟函数:方法一 procedure delay(msecs:inte ...
随机推荐
- 记一次lombok踩坑记
引言 今天中午正在带着耳机遨游在代码的世界里,被运营在群里@了,气冲冲的反问我最近有删生产的用户数据的吗?我肯定客气的回答道没有呀?生产的数据我怎么能随随便便可以删除,这可是公司的红线,再说了我也没有 ...
- [考试总结]noip模拟10
不小心有咕掉了一段时间 这次考试咕掉的分数也是太多了 然后就是这次暴力完全没有打满 遗憾啊遗憾 T1 入阵曲 前面的题目背景故意引导我们去往矩阵快速幂的方向去想 然而半毛钱关系没有 其实就是维护前缀和 ...
- 记一次系统崩溃事件【Mac版】
事件:Mac系统崩溃,导致电脑数据丢失,以及数据安全备份措施的不到位的教训! 解决措施: 1.开机后按:Command+R 按开机键 ,进入Mac 实用工具, 选择磁盘工具.由于没有备份直接抹掉磁盘. ...
- linux 之awk 次数统计
sort +awk+uniq 统计文件中出现次数 jps -v |grep jar|grep -v Jps|awk 'BEGIN{FS=".jar "} {print $1}' ...
- 性能测试之查看cpu命令
top -m 用户空间进程(us). 内核空间进程(sy). 高nice值的用户空间进程(ni). 空闲(id). 空闲等待io(wa). 中断上半部(hi). 中断下半部(si). 以及steal时 ...
- Scrapy+splash报错 Connection was refused by other side
报错信息如下: Traceback (most recent call last): File "/usr/local/lib/python3.7/site-packages/scrap ...
- js排序——sort()排序用法
sort() 方法用于对数组的元素进行排序,并返回数组.默认排序顺序是根据字符串Unicode码点. 语法:array.sort(fun):参数fun可选.规定排序顺序.必须是函数.注:如果调用该方法 ...
- labview系列-中级计算器开发
本例子通过对中级计算器的操练,实现对结构/字符串等基础知识的掌握和理解,为后续的编程工作提供基础. 计算器开发原理:通过按钮触发事件,再各个事件中编写相应加减乘除方法,并显示在结果中即可. 要点:临时 ...
- netty系列之:Event、Handler和Pipeline
目录 简介 ChannelPipeline ChannelHandler ChannelHandlerContext ChannelHandler中的状态变量 异步Handler 总结 简介 上一节我 ...
- Java进阶 | 从整体上观察面向对象
一.面向对象 面向对象是Java编程中最核心的思想,基本特征:继承.封装.多态. 1.特征之封装 将结构.数据.操作封装在对象实体中,使用时可以不关注对象内部结构,只能访问开放权限的功能入口,从而降低 ...