函数高级玩法
分享几个比较重要的知识点,web框架中大量的运用了,所以我给摘出来啦!
- 函数的嵌套
- 闭包
- 装饰器
1.函数嵌套
python中以函数为作用域,在作用域中定义的相关数据只能被当前作用域或子域使用
name = "隔壁老王" print(name ) def func(): print(name ) func()
1.函数在作用域中
实际上,函数也是定义在作用域中的数据,在执行函数时,同样遵循:优先在自己的作用域中寻找,没有则在上一级寻找
# 1. 在全局作用域定义了函数func def func(): print("你好") # 2.在全局作用域找到func函数并执行。 func() # 3.在全局作用域中定义了send函数 def send(): print("开始") # 优先在当前函数作用域找func函数,没有则向上级作用域中寻找。 func() print("结束") # 4.在全局作用域执行send函数 send()
上两个实例,更容易理解
def func(): print("你好") func() def send(): print("开始") func() print("结束") send() def func(): print(666) func()
def func(): print("你好") func() # 你好 def send(): print("开始") func() print("结束") def func(): # 会覆盖原来的func()函数,因为这个函数名和刚开始的函数名重名了 print(666) func() # 666 send() # 开始 # 666 # 结束
1.2函数定义的位置
上述示例中的函数定义在全局作用域中,其实函数也可以定义在局部在作用域中,这样函数被局部作用域和其子作用域中调用(函数的嵌套)。
def func(): print("111") def handler(): print("222") def inner(): print("333") inner() func() print("444") handler()
只要理解数据定义时所存在的作用域,并根据从上到下代码执行过程进行分析,再怎么嵌套都可以搞定。
现在的你可能有疑问:为什么要这么嵌套定义?把函数都定义在全局不好吗?
其实,大多数情况下我们都会将函数定义在全局,不会嵌套着定义函数。不过,当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这个N个函数放在全局会与其他函数名冲突时(尤其多人协同开发)可以选择使用函数的嵌套。
def f1(): pass def f2(): pass def func(): f1() f2()
def func(): def f1(): pass def f2(): pass f1() f2()
1.3 嵌套引发的作用域问题
基于内存和执行过程分析作用域
name = "eric" def run(): name = "job" def inner(): print(name) inner() run()
执行函数,内部会生成一个调用栈,理解成会创建一个作用域
name = "eric" def run(): name = "job" def inner(): print(name) return inner v1 = run() v1() v2 = run() v2()
三句话搞定作用域:
- 优先在自己的作用域找,自己没有就去上级作用域。
- 在作用域中寻找值时,要确保此次此刻值是什么。
- 分析函数的执行,并确定函数
作用域链
。(函数嵌套)
2.闭包
闭包 :基于函数的嵌套,可以将数据封装到一个包中,以后再去调用
应用场景:
- 1.封装到一个包中,供多个函数使用
- 2.封装到一个个独立的包中,供自己调用
闭包应用场景1:封装数据防止污染全局。(有的函数不需要,只给需要的函数提供)
name = "eric" def f1(): print(name, age) def f2(): print(name, age) def f3(): print(name, age) def f4(): pass
def func(age): name = "eric" def f1(): print(name, age) def f2(): print(name, age) def f3(): print(name, age) f1() f2() f3() func(123)
闭包应用场景2:封装数据封到一个包里,使用时在取。
def task(arg): def inner(): print(arg) return inner v1 = task(11) #创建一块自己的作用域 v2 = task(22) v3 = task(33) v1() v2() v3()
不多说,直接上案例
""" 基于多线程去下载视频 """ from concurrent.futures.thread import ThreadPoolExecutor import requests def download_video(url): res = requests.get( url=url, headers={ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS" } ) return res.content def outer(file_name): def write_file(response): # 上面的res.content会自动传递给response content = response.result() # 要拿取到里面的值必须要【.result】 with open(file_name, mode='wb') as file_object: file_object.write(content) return write_file POOL = ThreadPoolExecutor(10) # 创建线程池 video_dict = [ ("东北F4模仿秀.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f570000bvbmace0gvch7lo53oog"), ("卡特扣篮.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34q1g"), ] for item in video_dict: future = POOL.submit(download_video, url=item[1]) # 执行download_video方法,并传递参数url # outer(item[0]) 这里涉及到闭包的知识点 future.add_done_callback(outer(item[0])) # 上面执行完之后立刻执行括号中的方法【write_file函数】 POOL.shutdown()
3.装饰器
实现原理:基于@语法和闭包,将原函数封装到内部,并将函数赋值成新的函数【内层函数】,执行函数时在内层函数中执行闭包中封装的原函数
实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
适用场景:多个函数系统统一·在 执行前后自定义一些功能。
先提个小需求,在不改变源码之前,实现一个函数执行之前和执行之后分别输入"before"和"after"
对于刚刚入门的我,会这么写
def func(): print("before") print("我是func函数") value = [11, 22, 33] print("before") return value result = func()
对于我来说,我会这样做
def outer(origin): def inner(): print("before") res = origin() print("after") return inner def func(): print("我是func函数") value = [11, 22, 33, 44] return value res = outer(func) res()
如果涉及多个地方要同时在操作前和操作后进行分别输出
""" 由于代码是从上往下执行的,那么装饰器那里 eg: func1 = outer(func1) 此时,func1已经是inner函数,那么此时func1()是执行的inner函数, """ def outer(origin): def inner(): print("before") res = origin() print("after") return res return inner @outer # func1 = outer(func1) def func1(): print("我是func1函数") value = [11, 22, 33] return value @outer # func2 = outer(func2) def func2(): print("我是func2函数") value = [11, 22, 33] return value @outer # func3 = outer(func3) def func3(): print("我是func3函数") value = [11, 22, 33] return value func1() func2() func3()
下面,是我对装饰器进行了优化【函数带参数】
def outer(origin): def inner(*args,**kwargs): print("before") res = origin(*args,**kwargs) print("after") return res return inner @outer # func1 = outer(func1) def func1(a1): print("我是func1函数") value = [11, 22, 33] return value @outer # func2 = outer(func2) def func2(a2, a3): print("我是func2函数") value = [11, 22, 33] return value @outer # func3 = outer(func3) def func3(a4): print("我是func3函数") value = [11, 22, 33] return value func1(1) func2(2, a3=4) func3(5)
会发现装饰器实际上就是将原函数更改为其他的函数,然后再此函数中再去调用原函数。
好了,装饰器示例
def outer(origin): def inner(*args, **kwargs): # 执行前 res = origin(*args, **kwargs) # 执行后 return inner @outer def func(): pass func()
但是,上面的装饰器只能够解决百分之99的问题,下面介绍一个模块functools
加与不加的区别在于原函数.__name
和原函数.__doc
# 先认识不加的 def outer(origin): def inner(*args, **kwargs): """我是小淘气""" # 执行前 res = origin(*args, **kwargs) return res # 执行后 return inner @outer def func(): """我是图图""" value = [11, 22] return value print(func.__name__) # 目前func函数 -> inner函数 所以会打印inner函数的名称【inner】 print(func.__doc__) # 那么这么会打印inner函数的注释 【我是小淘气】
# 再认识加的 import functools def outer(origin): @functools.wraps(origin) def inner(*args, **kwargs): """我是小淘气""" # 执行前 res = origin(*args, **kwargs) return res # 执行后 return inner @outer def func(): """我是图图""" value = [11, 22] return value print(func.__name__) # 不管目前func是什么函数,我就打印原来函数的名称【func】 print(func.__doc__) # 那么这么会打印func函数的注释 【我是图图】
因为其实,一般情况下不用functools也可以实现装饰器的基本功能,但在项目开发时,不加functools会出错(内部会读取__name__
,且__name__
重名的话就报错)
好了,今天就写到这里叭,重新认识一下函数的高阶玩法!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步