|
1.函数嵌套
python中以函数为作用域,在作用域中定义的相关数据只能被当前作用域或子作用域使用。- NAME = "武沛齐"
- print(NAME)
- def func():
- print(NAME)
- func()
复制代码 1.1 函数在作用域中
其实,函数也是定义在作用域中的数据,在执行函数时候,也同样遵循:优先在自己作用域中寻找,没有则向上一接作用域寻找,例如:- # 1. 在全局作用域定义了函数func
- def func():
- print("你好")
- # 2. 在全局作用域找到func函数并执行。
- func()
- # 3.在全局作用域定义了execute函数
- def execute():
- print("开始")
- # 优先在当前函数作用域找func函数,没有则向上级作用域中寻找。
- func()
- print("结束")
- # 4.在全局作用域执行execute函数
- execute()
复制代码 此处,有一个易错点:作用域中的值在被调用时到底是啥?
- 情景1
- def func():
- print("你好")
- func()
- def execute():
- print("开始")
- func()
- print("结束")
- execute() # 打印你好
- def func():
- print(666)
- func()
复制代码 - 情景2
- def func():
- print("你好")
- func()
- def execute():
- print("开始")
- func()
- print("结束")
- def func():
- print(666)
- func()
- execute() # 打印666
复制代码 1.2 函数定义的位置
上述示例中的函数均定义在全局作用域,其实函数也可以定义在局部作用域,这样函数被局部作用域和其子作用域中调用(函数的嵌套)。- def func():
- print("沙河高晓松")
- def handler():
- print("昌平吴彦祖")
- def inner():
- print("朝阳大妈")
- inner()
- func()
- print("海淀网友")
- handler()
复制代码 到现在你会发现,只要理解数据定义时所存在的作用域,并根据从上到下代码执行过程进行分析,再怎么嵌套都可以搞定。
现在的你可能有疑问:为什么要这么嵌套定义?把函数都定义在全局不好吗?
其实,大多数情况下我们都会将函数定义在全局,不会嵌套着定义函数。不过,当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这个N个函数放在全局会与其他函数名冲突时(尤其多人协同开发)可以选择使用函数的嵌套。- def f1():
- pass
- def f2():
- pass
- def func():
- f1()
- f2()
复制代码- def func():
- def f1():
- pass
- def f2():
- pass
- f1()
- f2()
复制代码- """
- 生成图片验证码的示例代码,需要提前安装pillow模块(Python中操作图片的一个第三方模块)
- pip3 install pillow
- """
- import random
- from PIL import Image, ImageDraw, ImageFont
- def create_image_code(img_file_path, text=None, size=(120, 30), mode="RGB", bg_color=(255, 255, 255)):
- """ 生成一个图片验证码 """
- _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
- _upper_cases = _letter_cases.upper() # 大写字母
- _numbers = ''.join(map(str, range(3, 10))) # 数字
- chars = ''.join((_letter_cases, _upper_cases, _numbers))
- width, height = size # 宽高
- # 创建图形
- img = Image.new(mode, size, bg_color)
- draw = ImageDraw.Draw(img) # 创建画笔
- def get_chars():
- """生成给定长度的字符串,返回列表格式"""
- return random.sample(chars, 4)
- def create_lines():
- """绘制干扰线"""
- line_num = random.randint(*(1, 2)) # 干扰线条数
- for i in range(line_num):
- # 起始点
- begin = (random.randint(0, size[0]), random.randint(0, size[1]))
- # 结束点
- end = (random.randint(0, size[0]), random.randint(0, size[1]))
- draw.line([begin, end], fill=(0, 0, 0))
- def create_points():
- """绘制干扰点"""
- chance = min(100, max(0, int(2))) # 大小限制在[0, 100]
- for w in range(width):
- for h in range(height):
- tmp = random.randint(0, 100)
- if tmp > 100 - chance:
- draw.point((w, h), fill=(0, 0, 0))
- def create_code():
- """绘制验证码字符"""
- if text:
- code_string = text
- else:
- char_list = get_chars()
- code_string = ''.join(char_list) # 每个字符前后以空格隔开
- # Win系统字体
- # font = ImageFont.truetype(r"C:\Windows\Fonts\SEGOEPR.TTF", size=24)
- # Mac系统字体
- # font = ImageFont.truetype("/System/Library/Fonts/SFNSRounded.ttf", size=24)
- # 项目字体文件
- font = ImageFont.truetype("MSYH.TTC", size=15)
- draw.text([0, 0], code_string, "red", font=font)
- return code_string
- create_lines()
- create_points()
- code = create_code()
- # 将图片写入到文件
- with open(img_file_path, mode='wb') as img_object:
- img.save(img_object)
- return code
- code = create_image_code("a2.png")
- print(code)
复制代码 1.3 嵌套引发的作用域问题
基于内存和执行过程分析作用域。- name = "武沛齐"
- def run():
- name = "alex"
- def inner():
- print(name)
- inner()
- run()
复制代码- name = "武沛齐"
- def run():
- name = "alex"
- def inner():
- print(name)
- return inner
- v1 = run()
- v1()
- v2 = run()
- v2()
复制代码- name = "武沛齐"
- def run():
- name = "alex"
- def inner():
- print(name)
- return [inner,inner,inner]
- func_list = run()
- func_list[2]()
- func_list[1]()
- funcs = run()
- funcs[2]()
- funcs[1]()
复制代码
三句话搞定作用域:
- 优先在自己的作用域找,自己没有就去上级作用域。
- 在作用域中寻找值时,要确保此次此刻值是什么。
- 分析函数的执行,并确定函数作用域链。(函数嵌套)
练习题
- 分析代码,写结果
- name = '武沛齐'
- def func():
- def inner():
- print(name)
- res = inner()
- return res
- v = func() # 打印武沛齐
- print(v) # None
复制代码 - 分析代码,写结果
- name = '武沛齐'
- def func():
- def inner():
- print(name)
- return "alex"
- res = inner()
- return res
- v = func() # 打印武沛齐
- print(v) # alex
复制代码 - 分析代码,写结果
- name = 'root'
- def func():
- def inner():
- print(name)
- return 'admin'
- return inner
- v = func() # v=inner
- result = v() # 打印root
- print(result) # admin
复制代码 - 分析代码,写结果
- def func():
- name = '武沛齐'
- def inner():
- print(name)
- return '路飞'
- return inner
- v11 = func()
- data = v11() # 打印武沛齐
- print(data) # 路飞
- v2 = func()() # 打印武沛齐
- print(v2) # 路飞
复制代码 - 分析代码,写结果
- def func(name):
- # name="alex"
- def inner():
- print(name)
- return 'luffy'
- return inner
- v1 = func('武沛齐')() # 打印武沛齐
- print(v1) # luffy
- v2 = func('alex')() # 打印alex
- print(v2) # luffy
复制代码
- def func(name):
- def inner():
- print(name)
- return 'luffy'
- return inner
- v1 = func('武沛齐')
- v2 = func('alex')
- v1() # 打印武沛齐
- v2() # 打印alex
复制代码
- def func(name=None):
- if not name:
- name= '武沛齐'
- def inner():
- print(name)
- return 'root'
- return inner
- v1 = func()() # 打印武沛齐
- v2 = func('alex')() # 打印alex
- print(v1,v2) # v1=root,v2=root
复制代码
2.闭包
闭包,简而言之就是将数据封装在一个包(区域)中,使用时再去里面取。(本质上 闭包是基于函数嵌套搞出来一个中特殊嵌套)
- 闭包应用场景1:封装数据防止污染全局。
- name = "武沛齐"
- def f1():
- print(name, age)
- def f2():
- print(name, age)
- def f3():
- print(name, age)
- def f4():
- pass
复制代码- def func(age):
- name = "武沛齐"
- 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()
复制代码- def task(arg):
- def inner():
- print(arg)
- return inner
- inner_func_list = []
- for val in [11,22,33]:
- inner_func_list.append( task(val) )
- inner_func_list[0]() # 11
- inner_func_list[1]() # 22
- inner_func_list[2]() # 33
复制代码- """ 基于多线程去下载视频 """
- 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):
- content = response.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"),
- ("罗斯mvp.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg")
- ]
- for item in video_dict:
- future = POOL.submit(download_video, url=item[1])
- future.add_done_callback(outer(item[0]))
- POOL.shutdown()
复制代码
3.装饰器
现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入 "before" 和 "after"。- def func():
- print("我是func函数")
- value = (11,22,33,44)
- return value
- result = func()
- print(result)
复制代码 3.1 第一回合
你的实现思路:- def func():
- print("before")
- print("我是func函数")
- value = (11,22,33,44)
- print("after")
- return value
- result = func()
复制代码 我的实现思路:- def func():
- print("我是func函数")
- value = (11, 22, 33, 44)
- return value
- def outer(origin):
- def inner():
- print('inner')
- origin()
- print("after")
- return inner
- func = outer(func)
- result = func()
复制代码 处理返回值:- def func():
- print("我是func函数")
- value = (11, 22, 33, 44)
- return value
- def outer(origin):
- def inner():
- print('inner')
- res = origin()
- print("after")
- return res
- return inner
- func = outer(func)
- result = func()
复制代码 3.2 第二回合
在Python中有个一个特殊的语法:在某个函数上方使用@函数名,
@函数名
def xxx():
pass
则python内部会自动执行函数名(xxx),执行完之后,再将结果赋值给xxx
- def outer(origin):
- def inner():
- print('inner')
- res = origin()
- print("after")
- return res
- return inner
- @outer
- def func():
- print("我是func函数")
- value = (11, 22, 33, 44)
- return value
- func()
复制代码 3.3 第三回合
请在这3个函数执行前和执行后分别输入 "before" 和 "after"- def func1():
- print("我是func1函数")
- value = (11, 22, 33, 44)
- return value
- def func2():
- print("我是func2函数")
- value = (11, 22, 33, 44)
- return value
- def func3():
- print("我是func3函数")
- value = (11, 22, 33, 44)
- return value
- func1()
- func2()
- func3()
复制代码 你的实现思路:- def func1():
- print('before')
- print("我是func1函数")
- value = (11, 22, 33, 44)
- print("after")
- return value
- def func2():
- print('before')
- print("我是func2函数")
- value = (11, 22, 33, 44)
- print("after")
- return value
- def func3():
- print('before')
- print("我是func3函数")
- value = (11, 22, 33, 44)
- print("after")
- return value
- func1()
- func2()
- func3()
复制代码 我的实现思路:- def outer(origin):
- def inner():
- print("before 110")
- res = origin() # 调用原来的func函数
- print("after")
- return res
- return inner
- @outer
- def func1():
- print("我是func1函数")
- value = (11, 22, 33, 44)
- return value
- @outer
- def func2():
- print("我是func2函数")
- value = (11, 22, 33, 44)
- return value
- @outer
- def func3():
- print("我是func3函数")
- value = (11, 22, 33, 44)
- return value
- func1()
- func2()
- func3()
复制代码 装饰器,在不修改原函数内容的前提下,通过@函数可以实现在函数前后自定义执行一些功能(批量操作会更有意义)。
优化
优化以支持多个参数的情况。- def outer(origin):
- def inner(*args, **kwargs):
- print("before 110")
- res = origin(*args, **kwargs) # 调用原来的func函数
- print("after")
- return res
- return inner
- @outer # func1 = outer(func1)
- def func1(a1):
- print("我是func1函数")
- value = (11, 22, 33, 44)
- return value
- @outer # func2 = outer(func2)
- def func2(a1, a2):
- print("我是func2函数")
- value = (11, 22, 33, 44)
- return value
- @outer # func3 = outer(func3)
- def func3(a1):
- print("我是func3函数")
- value = (11, 22, 33, 44)
- return value
- func1(1)
- func2(11, a2=22)
- func3(999)
复制代码 其中,我的那种写法就称为装饰器。
- 实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。
- 实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
- 适用场景:多个函数系统统一在 执行前后自定义一些功能。
- 装饰器示例
- def outer(origin):
- def inner(*args, **kwargs):
- # 执行前
- res = origin(*args, **kwargs) # 调用原来的func函数
- # 执行后
- return res
- return inner
- @outer
- def func():
- pass
- func()
复制代码 伪应用场景
在以后编写一个网站时,如果项目共有100个页面,其中99个是需要登录成功之后才有权限访问,就可以基于装饰器来实现。基于第三方模块Flask(框架)快速写一个网站:- from flask import Flask
- app = Flask(__name__)
- def index():
- return "首页"
- def info():
- return "用户中心"
- def order():
- return "订单中心"
- def login():
- return "登录页面"
- app.add_url_rule("/index/", view_func=index)
- app.add_url_rule("/info/", view_func=info)
- app.add_url_rule("/login/", view_func=login)
- app.run()
复制代码 基于装饰器实现的伪代码:- from flask import Flask
- app = Flask(__name__)
- def auth(func):
- def inner(*args, **kwargs):
- # 在此处,判断如果用户是否已经登录,已登录则继续往下,未登录则自动跳转到登录页面。
- return func(*args, **kwargs)
- return inner
- @auth
- def index():
- return "首页"
- @auth
- def info():
- return "用户中心"
- @auth
- def order():
- return "订单中心"
- def login():
- return "登录页面"
- app.add_url_rule("/index/", view_func=index, endpoint='index')
- app.add_url_rule("/info/", view_func=info, endpoint='info')
- app.add_url_rule("/order/", view_func=order, endpoint='order')
- app.add_url_rule("/login/", view_func=login, endpoint='login')
- app.run()
复制代码 重要补充:functools
你会发现装饰器实际上就是将原函数更改为其他的函数,然后再此函数中再去调用原函数。- def handler():
- pass
- handler()
- print(handler.__name__) # handler
复制代码- def auth(func):
- def inner(*args, **kwargs):
- return func(*args, **kwargs)
- return inner
- @auth
- def handler():
- pass
- handler()
- print(handler.__name__) # inner
复制代码- import functools
- def auth(func):
- @functools.wraps(func)
- def inner(*args, **kwargs):
- return func(*args, **kwargs)
- return inner
- @auth
- def handler():
- pass
- handler()
- print(handler.__name__) # handler
复制代码 其实,一般情况下大家不用functools也可以实现装饰器的基本功能,但后期在项目开发时,不加functools会出错(内部会读取__name__,且__name__重名的话就报错),所以在此大家就要规范起来自己的写法。- import functools
- def auth(func):
- @functools.wraps(func) # inner.__name__=func.__name__,inner.__doc__=func.__doc__
- def inner(*args, **kwargs):
- """巴巴里吧"""
- res = func(*args, **kwargs) # 执行原函数
- return res
- return inner
复制代码 总结
- 函数可以定义在全局、也可以定义另外一个函数中(函数的嵌套)
- 学会分析函数执行的步骤(内存中作用域的管理)
- 闭包,基于函数的嵌套,可以将数据封装到一个包中,以后再去调用。
- 装饰器
- 实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。
- 实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
- 适用场景:多个函数系统统一在 执行前后自定义一些功能。
- 装饰器示例
- import functools
- def auth(func):
- @functools.wraps(func)
- def inner(*args, **kwargs):
- """巴巴里吧"""
- res = func(*args, **kwargs) # 执行原函数
- return res
- return inner
复制代码
练习
- 请为以下所有函数编写一个装饰器,添加上装饰器后可以实现:执行func时,先执行func函数内部代码,再输出 "after"
- def func(a1):
- return a1 + "傻叉"
- def base(a1,a2):
- return a1 + a2 + '傻缺'
- def foo(a1,a2,a3,a4):
- return a1 + a2 + a3 + a4 + '傻蛋'
复制代码- import functools
- def dec(origin):
- @functools.wraps(origin)
- def inner(*args, **kwargs):
- res = origin(*args, **kwargs)
- print("after")
- return res
- return inner
- @dec
- def func(a1):
- return a1 + "傻叉"
-
-
- @dec
- def base(a1, a2):
- return a1 + a2 + '傻缺'
-
-
- @dec
- def foo(a1, a2, a3, a4):
- return a1 + a2 + a3 + a4 + '傻蛋'
-
-
- func("1")
- base("1", "2")
- foo("1", "2", "3", "4")
复制代码 - 请为以下所有函数编写一个装饰器,添加上装饰器后可以实现:将被装饰的函数执行5次,讲每次执行函数的结果按照顺序放到列表中,最终返回列表。
- import random
- def func():
- return random.randint(1,4)
- result = func() # 内部自动执行5次,并将每次执行的结果追加到列表最终返回给result
- print(result)
复制代码- import random
- import functools
-
-
- def dec(origin):
- @functools.wraps(origin)
- def inner(*args, **kwargs):
- data_list = []
- for i in range(5):
- res = origin(*args, **kwargs)
- data_list.append(res)
- return data_list
-
- return inner
-
-
- @dec
- def func():
- return random.randint(1, 4)
-
-
- result = func() # 内部自动执行5次,并将每次执行的结果追加到列表最终返回给result
- print(result)
复制代码 - 请为以下函数编写一个装饰器,添加上装饰器后可以实现: 检查文件所在路径(文件件)是否存在,如果不存在自动创建文件夹(保证写入文件不报错)。
- def write_user_info(path):
- file_obj = open(path, mode='w', encoding='utf-8')
- file_obj.write("武沛齐")
- file_obj.close()
- write_user_info('/usr/bin/xxx/xxx.png')
复制代码- import functools
- import os
-
-
- def check(origin):
- @functools.wraps(origin)
- def inner(path):
- # 获取上级路径
- folder_path = os.path.dirname(path)
- if not os.path.exists(folder_path):
- os.makedirs(folder_path)
- res = origin(path)
- return res
-
- return inner
-
-
- @check
- def write_user_info(path):
- file_obj = open(path, mode='w', encoding='utf-8')
- file_obj.write("武沛齐")
- file_obj.close()
-
-
- write_user_info('files/xxx.png')
复制代码 - 分析代码写结果:
- def get_data():
- scores = []
- def inner(val):
- scores.append(val)
- print(scores)
- return inner
- func = get_data()
- func(10) # [10]
- func(20) # [10, 20]
- func(30) # [10, 20, 30]
复制代码 - 看代码写结果
- name = "武沛齐"
- def foo():
- print(name)
- def func():
- name = "root"
- foo()
- func() # 武沛齐
复制代码 - 看代码写结果
- name = "武沛齐"
- def func():
- name = "root"
- def foo():
- print(name)
- foo()
- func() # root
复制代码 - 看代码写结果
- def func(val):
- def inner(a1, a2):
- return a1 + a2 + val
- return inner
- data_list = []
- for i in range(10):
- data_list.append( func(i) )
- print(data_list) # [<function func.<locals>.inner at 0x0000024AB7733280>, <function func.<locals>.inner at 0x0000024AC82E63A0>, <function func.<locals>.inner at 0x0000024AC82E6550>, <function func.<locals>.inner at 0x0000024AC8BF5940>, <function func.<locals>.inner at 0x0000024AC8BF59D0>, <function func.<locals>.inner at 0x0000024AC8BF5A60>, <function func.<locals>.inner at 0x0000024AC8BF5AF0>, <function func.<locals>.inner at 0x0000024AC8BF5B80>, <function func.<locals>.inner at 0x0000024AC8BF5C10>, <function func.<locals>.inner at 0x0000024AC8BF5CA0>]
复制代码 - 看代码写结果
- def func(val):
- def inner(a1, a2):
- return a1 + a2 + val
- return inner
- data_list = []
- for i in range(10):
- data_list.append(func(i))
- v1 = data_list[0](11,22)
- v2 = data_list[2](33,11)
- print(v1) # 33
- print(v2) # 46
复制代码
来源:https://www.cnblogs.com/sbhglqy/p/18114137
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|