IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python也可以拥有延迟函数 -> 正文阅读

[Python知识库]Python也可以拥有延迟函数

延迟函数defer

我们知道在Golang中有一个关键字defer,用它来声明在函数调用前,会让函数*延迟**到外部函数退出时再执行,注意,这里的退出含义:函数return返回或者函数panic退出

defer的特性

  1. defer函数会在外层函数执行结束后执行
package main

import "fmt"

func main() {
	defer fmt.Println(2)
	fmt.Println(1)

}
/* output:
1
2
*/
  1. 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
*/

总结

  1. defer函数总会被执行,无论外层函数是正常退出还是异常panic
  2. 如果函数中有多个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中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:35:59  更:2021-07-29 11:36:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 10:07:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码