Python学习笔记---day12函数进阶
day12函数进阶
- 函数的嵌套
- 闭包
- 装饰器
上述内容均属于函数部分必备知识,以后开发时直接和间接都会使用,请务必理解(重在理解,不要去死记硬背)。
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(): # 覆盖前面那个func()print(666)func()
情景2
def func():print("你好")func()def execute():print("开始")func()print("结束")def func():print(666)func() execute()
1.2 函数定义的位置
上述示例中的函数均定义在全局作用域,其实函数也可以定义在局部作用域,这样函数被局部作用域和其子作用于中调用(函数的嵌套)。
def func():print("沙河高晓松")def handler():print("昌平吴彦祖")def inner():print("朝阳大妈")inner()func()print("海淀网友")handler()
到现在你会发现,只要理解数据定义时所存在的作用域,并根据从上到下代码执行过程进行分析,再怎么嵌套都可以搞定。
现在的你可能有疑问:为什么要这么嵌套定义?把函数都定义在全局不好吗?
其实,大多数情况下我们都会将函数定义在全局,不会嵌套着定义函数。不过,当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这个N个函数放在全局会与其他函数名冲突时(尤其多人协同开发)可以选择使用函数的嵌套。
def f1():passdef f2():passdef func():f1()f2()
def func():def f1():passdef f2():passf1()f2()
"""
生成图片验证码的示例代码,需要提前安装pillow模块(Python中操作图片中一个第三方模块)pip3 install pillow
"""
import random
from PIL import Image, ImageDraw, ImageFontdef 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 = textelse: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_stringcreate_lines()create_points()code = create_code()# 将图片写入到文件with open(img_file_path, mode='wb') as img_object:img.save(img_object)return codecode = 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 resv = func() print(v)# 刘小伟 # None
分析代码,写结果
name = '刘小伟'def func():def inner():print(name)return "alex"res = inner()return resv = func() print(v)# 刘小伟 # alex
分析代码,写结果
name = 'root'def func():def inner():print(name)return 'admin'return innerv = func() result = v() print(result)# root # admin
分析代码,写结果
def func():name = '刘小伟'def inner():print(name)return '路飞'return innerv11 = func() data = v11() print(data)v2 = func()() print(v2)
分析代码,写结果
def func(name):# name="alex"def inner():print(name)return 'luffy'return innerv1 = func('刘小伟')() print(v1)v2 = func('alex')() print(v2)
分析代码,写结果
def func(name=None):if not name:name= '刘小伟'def inner():print(name)return 'root'return innerv1 = func()() v2 = func('alex')() print(v1,v2)
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 innerv1 = task(11) v2 = task(22) v3 = task(33) v1() v2() v3()
def task(arg):def inner():print(arg)return innerinner_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 ThreadPoolExecutorimport requestsdef task(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_filePOOL = 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:# 去线程池取一个人,让这个人去下载视频,让这个人去执行task函数(函数内部定义下载逻辑)POOL.submit(task, url=item[1])# 当执行完成task函数(下载完成)之后自动执行某个函数future = POOL.submit(download_video, url=item[1])future.add_done_callback(outer(item[0])) # outer(item[0])执行返回一个done函数POOL.shutdown()
3.装饰器
现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入 “before” 和 “after”。
def func():print("我是func函数")value = (11,22,33,44) return valueresult = func()
print(result)
3.1 第一回合
你的实现思路:
def func():print("before")print("我是func函数")value = (11,22,33,44) print("after")return valueresult = func()
我的实现思路:
def func():print("我是func函数")value = (11, 22, 33, 44)return valuedef outer(origin):def inner():print('inner')origin()print("after")return innerfunc = outer(func)
result = func()
处理返回值:
def func():print("我是func函数")value = (11, 22, 33, 44)return valuedef outer(origin):def inner():print('before')res = origin() # 调用原来的func函数, 返回valueprint("after")return resreturn innerfunc = outer(func)
result = func()
3.2 第二回合
在Python中有个一个特殊的语法糖:
"""
在python中支持特殊语法,在某个函数上方使用:
@函数名
def xxx():pass
python内部会自动执行这个函数名(),执行完之后,再将结果赋值给xxx
xxx = 函数名(xxx)
"""
def outer(origin):def inner():print('before')res = origin()print("after")return resreturn inner@outer # func = outer(func) # 返回inner
def func():print("我是func函数")value = (11, 22, 33, 44)return valuefunc() # 执行inner()
#输出
before
我是func函数
after
3.3 第三回合
请在这3个函数执行前和执行后分别输入 “before” 和 “after”
def func1():print("我是func1函数")value = (11, 22, 33, 44)return valuedef func2():print("我是func2函数")value = (11, 22, 33, 44)return valuedef func3():print("我是func3函数")value = (11, 22, 33, 44)return valuefunc1()
func2()
func3()
你的实现思路:
def func1():print('before')print("我是func1函数")value = (11, 22, 33, 44)print("after")return valuedef func2():print('before')print("我是func2函数")value = (11, 22, 33, 44)print("after")return valuedef func3():print('before')print("我是func3函数")value = (11, 22, 33, 44)print("after")return valuefunc1()
func2()
func3()
def outer(origin):def inner():print('before')res = origin()print("after")return inner@outer
def func():print('我是func1函数')value = (11, 22, 33, 44)return valuefunc()
def outer(origin):def inner():print("before 110")res = origin() # 调用原来的func函数print("after")return resreturn 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 valuefunc1()
func2()
func3()
装饰器,在不修改原函数内容的前提下,通过@函数可以实现在函数前后自定义执行一些功能(批量操作会更有意义)。
优化
优化以支持多个参数的情况。
def outer(origin):def inner(*args, **kwargs):print("before 110")res = origin(*args, **kwargs) # 调用原来的func函数print("after")return resreturn 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 valuefunc1(1)
func2(11, a2=22)
func3(999)
实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。
实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
适用场景:多个函数系统统一在 执行前后自定义一些功能。
装饰器示例
def outer(origin):def inner(*args, **kwargs):# 执行前res = origin(*args, **kwargs) # 调用原来的func函数# 执行后return resreturn inner@outer def func():passfunc()
伪应用场景
在以后编写一个网站时,如果项目共有100个页面,其中99个是需要登录成功之后才有权限访问,就可以基于装饰器来实现。
pip3 install flask
基于第三方模块Flask(框架)快速写一个网站:
from flask import Flaskapp = 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 Flaskapp = 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():passhandler()
print(handler.__name__) # handler
def auth(func):def inner(*args, **kwargs):return func(*args, **kwargs)return inner@auth
def handler():passhandler()
print(handler.__name__) # inner
import functoolsdef auth(func):@functools.wraps(func)def inner(*args, **kwargs):return func(*args, **kwargs)return inner@auth
def handler():passhandler()
print(handler.__name__) # handler
其实,一般情况下大家不用functools也可以实现装饰器的基本功能,但后期在项目开发时,不加functools会出错(内部会读取__name__
,且__name__
重名的话就报错),所以在此大家就要规范起来自己的写法。
import functoolsdef auth(func):@functools.wraps(func)def inner(*args, **kwargs):"""巴巴里吧"""res = func(*args, **kwargs) # 执行原函数return resreturn inner
总结
函数可以定义在全局、也可以定义另外一个函数中(函数的嵌套)
学会分析函数执行的步骤(内存中作用域的管理)
闭包,基于函数的嵌套,可以将数据封装到一个包中,以后再去调用。
装饰器
实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。
实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
适用场景:多个函数系统统一在 执行前后自定义一些功能。
装饰器示例
import functoolsdef auth(func):@functools.wraps(func)def inner(*args, **kwargs):"""巴巴里吧"""res = func(*args, **kwargs) # 执行原函数return resreturn inner
Python学习笔记---day12函数进阶相关推荐
- Python学习笔记:函数(Function)
Python学习笔记:函数(Function) 一.函数基本概念 函数是Python里组织与重用代码最重要的方法.一般来说,如果你期望多次重复相同或相似的代码,写一个可重用的函数可能是值得的.函数通过 ...
- Python学习笔记12_函数
Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...
- Python学习笔记——一些函数
本文对应头歌上的Python练习:https://www.educoder.net/paths/pn7qklv9 基础知识1: input( )函数 input()函数从控制台获得用户输入,无论用户在 ...
- 小甲鱼python003答案_小甲鱼:Python学习笔记003_函数
>>> # 函数 >>> def myFirstFunction(params1,params2...): print("这是我的第一个函数!" ...
- Python学习笔记系列——函数
今年下半年的计划主要是Python和Mysql了,公司不方便看书和视频,就照着廖雪峰的Python网站开始看了.以下纯为个人笔记记录,若是想系统学习的小伙伴还是看这里的好一些,毕竟系统.https:/ ...
- Python学习笔记:匿名函数
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- Python学习笔记:函数
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- python学习笔记三一 函数学习
函数学习 range函数 •生成有序的序列 •生成数字队列可以定制 # range函数案例1 # 生成一个从1到10的数字序列 # range的生成序列的两个面数字是左包括,右不包括(如下所示只包括1 ...
- Python学习笔记之函数(四)
1.传递任意数量的实参,可以在形参的参数底下加个 * 符号,在我们预先不知道要函数要接受多少实参的时候,就可以使用这种方式,函数就可以从调用语句中收集任意数量的实参. 示例代码如下: #传递任意数量的 ...
最新文章
- 【BZOJ3994】[SDOI2015]约数个数和 莫比乌斯反演
- python中显示第三行数据_Python从零开始第三章数据处理与分析①python中的dplyr(1)...
- 在Mapnik中显示中文(网上资料整理)
- kaggle图像比赛中的submission文件格式解析
- 【c】‘声明’和malloc不要重复开辟空间
- Spring Boot 2.3.3 正式发布!
- 20210424:力扣第237周周赛(下)
- Red5流服务器搭建(实现在线直播,流媒体视频播放和在线视频会议)
- linux安装eclipse教程,Linux下的Eclipse安装
- Pandas常见筛选数据的五种方法其一逻辑筛选。看见必懂,懂者必会,会者必加分
- 天池竞赛-资金流入流出预测总结
- Swagger简单使用之从入门到精通
- Kali google 翻译
- Java如何将word转成html_[JavaWeb基础] 025.JAVA把word转换成html
- 探讨“对称数”的判断算法
- github访问-steam++ 问题解决
- python循环结构高一信息技术_高中信息技术《程序的循环结构(1)》优质课教学设计、教案...
- android toast设置背景颜色,Android 彩色Toast的实现代码
- 淘宝/天猫上传图片到淘宝 API 返回值说明(upload_img)
- 【行业基础】什么是波形