工作之后,逐渐接触到了回调函数的应用。一开始我始终无法理解回调函数的执行顺序和意义,并且认为从可读性和执行效果上,并没有优于顺序执行的代码。不过后面对设计模式有一些了解后,对回调函数的概念大概有了一些模糊的理解,因此在这里整理一下以供学习。
回调函数 回调函数的主要特点是,将一个函数作为参数传入另外一个函数。并且在主函数执行完之后再继续调用该函数。
它和普通顺序写的函数相比有什么不同呢?在我的理解中,主要是能够有更好的扩展性。
例如以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def data_processor (data ): """ 处理数据的函数。直接在函数内部处理成功和失败的逻辑。 """ print (f"Processing data: {data} " ) if len (data) > 5 : processed_data = data.upper() print (f"Data processed successfully: {processed_data} " ) else : error_message = "Data is too short." print (f"Error processing data: {error_message} " ) data_processor("Hello World" ) data_processor("Hi" )
上述代码在一个顺序执行的函数中判断是否处理数据。现在换成用回调函数来执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 def data_processor (data, success_callback, error_callback ): """ 处理数据的函数。根据数据处理的结果,调用相应的回调函数。 """ print (f"Processing data: {data} " ) if len (data) > 5 : processed_data = data.upper() success_callback(processed_data) else : error_message = "Data is too short." error_callback(error_message) def on_success (result ): """ 数据处理成功时的回调函数。 """ print (f"Data processed successfully: {result} " )def on_error (error ): """ 数据处理失败时的回调函数。 """ print (f"Error processing data: {error} " ) data_processor("Hello World" , on_success, on_error) data_processor("Hi" , on_success, on_error)
这样有什么好处?
可以灵活的修改各自情况下的回调函数,不然就需要一直在主函数中修改。
所有逻辑包含在主函数中,并不利于维护。改成回调函数后,更利于调整逻辑。
异步操作 回调函数的另一个用法是在异步编程中使用,来避免操作阻塞程序。这种用法是在一个操作完成时(例如,网络请求、读取文件等),执行后续的操作。
以下是示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import asyncioimport aiohttpasync def fetch_data (url ): """ 异步获取URL的数据 """ print (f"Starting to fetch data from {url} " ) async with aiohttp.ClientSession() as session: async with session.get(url) as response: data = await response.text() return dataasync def on_data_fetched (data ): """ 模拟回调函数:数据获取后被调用 """ print ("Data fetched: " ) print (data[:100 ]) async def main (): """ 主函数,执行异步的数据获取操作,并在完成后处理数据 """ url = "https://www.example.com" data = await fetch_data(url) await on_data_fetched(data) asyncio.run(main())
上述代码中,on_data_fetched
函数在fetch_data
函数完成后被调用。它的好处是可以在等待执行的时候运行其他的程序部分。
闭包 相比于回调函数,闭包这个概念更多的涉及到作用域。它允许函数访问并操作函数外部的变量。我的理解是,它的作用是创建一个定义作用域,这个作用域能够获取外部的变量。
以下是一个示例:
1 2 3 4 5 6 7 8 def foo (): a = 'free var' def bar (): print (a) return bar foo()()
可以看到,bar
函数能够读取到外部的a
变量,这就是闭包。
它和之前看到的装饰器有什么区别?我在查阅了一些资料后得到以下的结论:
装饰器是闭包思想的一种实现。而闭包的定义是在函数内定义另一个函数。内函数能够调用外函数的临时变量。
例如给一个示例闭包函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def outfun (outNum ): print ('---start outfun---' ) def infun (inNum ): print ('---start infun---' ) print (outNum + inNum) print ('---end infun---' ) print ('---end outfun---' ) return infun ret = outfun(10 ) ret(20 )
上面的ret
本身是一个函数,它指向infun()
,并且将其中的outNum
定义为10。之后在调用ret
时输入的参数则充当了infun
的参数。
我们再来示范一下装饰器的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 def test_func1 (): print ('Begin func1...' )def test_func2 (): print ('Begin func2...' ) def check (function ): def check_inner (): print ('Checking Inner Function...' ) function() return check_inner check_test1 = check(test_func1) check_test1()@check def test_func1 (): print ('Begin func1...' )@check def test_func2 (): print ('Begin func2...' ) test_func1() test_func2()
在check
函数中,我们设定了一个内函数,它接收外部的函数作为参数,指向内部的函数。在内部函数中对外部函数进行了一系列处理。这就是装饰器的简单用法。
更常用的用法是不定长参数的使用,例如:
1 2 3 4 def deco (func ): def new_func (*args, **kwargs ): return func(*args, **kwargs) return new_func
以及接收参数的装饰器工厂:
1 2 3 4 5 6 7 8 9 def deco_factory (*args, **kwargs ): def deco (func ): print (args) return func return deco@deco_factory('factory' ) def foo (): pass
这样就能灵活修改装饰器的方法。
最后,记录一个接收参数的装饰器用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import timedef outer (choose ): if choose==1 : def deco (func ): def inner (*arg, **kwarg ): print ('decoration1' ) begin_time = time.time() time.sleep(2 ) a = func(*arg, **kwarg) end_time = time.time() print ('运行时间1:' , end_time - begin_time) return a return inner return deco else : def deco (func ): def inner (*arg, **kwarg ): print ('decoration2' ) begin_time = time.time() time.sleep(5 ) a = func(*arg, **kwarg) end_time = time.time() print ('运行时间2:' , end_time - begin_time) return a return inner return deco@outer(1 ) def test1 (a ): print ('test function1:' , a) return a@outer(5 ) def test2 (a ): print ('test function2:' , a) return a test1(2 ) decoration1 test function1: 2 运行时间1 : 2.000072717666626 test2(4 ) decoration2 test function2: 4 运行时间2 : 5.000797986984253
2024/4/3 于苏州