python之函数闭包、可迭代对象和迭代器
一、函数名的应用
# 1,函数名就是函数的内存地址,而函数名()则是运行这个函数。 def func(): return print(func) # 返回一个地址 # 2,函数名可以作为变量。 def func1(): print(666) f1 = func1 f2 = f1 f2() # 就等于func1() 此时执行函数 # 3,函数名可以作为函数的参数。 def func1(): print(666) def func2(x): x() func2(func1) # 输出666 func1作为实参传进func2中,此时在func2中x()等于func1() # 4,函数名可以当做函数的返回值。 def wraaper(): def inner(): print(666) return inner ret = wraaper() # 执行函数wraaper(),得到返回值inner赋值给ret,这个inner是函数名 ret() # 输出666,ret()等于inner() # (注意:这里要调用inner()只能是用ret(),因为inner()是wraaper()的嵌套函数,在全局命名空间中并没有声明,所以直接用inner()不能执行,) def func2(): print('in func2') def func3(x): print('in func3') return x f = func3(func2) # 给func3传进一个参数func2,返回func2赋值给f f() # f()等于func2() # 5,函数名可以作为容器类类型的元素。 def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1, f2, f3] d = {'f1': f1, 'f2': f2, 'f3': f3} # 调用 l[0]() # 输出 f1 d['f2']() # 输出 f2 def func1(): print('in func1') def func2(): print('in func2') def func3(): print('in func3') def func4(): print('in func4') l1 = [func1,func2,func3,func4] #调用 for i in l1: i() # 像上面的函数名这种,称为第一类对象。 # 第一类对象(first-class object)指: # 1.可在运行期创建 # 2.可用作函数参数或返回值 # 3.可存入变量的实体。 # (如果不明白,那就记住一句话,就当普通变量用)
globals() locals()
globals() #返回全局变量(包含内置函数)的一个字典。
locals() #返回当前位置的变量的字典。
例如:
def func1(): a = 2 b = 3 print(globals()) # 此时globals()在局部命名空间中,但也是返回全局命名空间的一个字典 print(locals()) # 此时locals()在局部命名空间中,所以返回的是局部命名空间的一个字典{'b': 3, 'a': 2} print(globals()) # 此时globals()在全局命名空间中,返回全局命名空间的一个字典 print(locals()) # 此时locals()在全局命名空间中,所以返回的是全局命名空间的一个字典 func1() # 结果: # {'__name__': '__main__', '__doc__': None(后面还有一大推东西,省略)...} # {'__name__': '__main__', '__doc__': None(后面还有一大推东西,省略)...} # {'__name__': '__main__', '__doc__': None(后面还有一大推东西,省略)...} # {'b': 3, 'a': 2}
总结:
globals()无论在哪个命名空间,返回的都是全局命名空间的一个字典
locals()在哪个命名空间就返回哪个命名空间的字典
def func1(): a = 2 b = 3 def inner(): c = 5 d = 6 print(globals()) #{'__name__': '__main__', '__doc__': None(后面还有一大推东西,省略)...} print(locals()) #{'c': 5, 'd': 6} inner() func1()
二、闭包
1、内层函数引用外层函数的变量(非全局变量),这样该内部函数称就称为闭包函数。
如何创建闭包
- 闭包函数必须有内嵌函数
- 内嵌函数需要引用该嵌套函数上一级空间中的变量
def wraaper(): name = '鬼见愁' def inner(): print(name) return inner f = wraaper() f() def game(game_name): def hero(name): print("%s: %s" % (game_name, name)) return hero my_game = game("英雄联盟") my_game("盖伦") my_game("盲仔") my_game2 = game("地下城与勇士") my_game2("剑圣")
2、判断闭包函数的方法__closure__
# 1,是闭包返回值会有cell元素 def wraaper(): name = '鬼见愁' def inner(): print(name) print(inner.__closure__) return inner f = wraaper() f() # 2,不是闭包就会返回None name = '鬼见愁' def wraaper(): def inner(): print(name) print(inner.__closure__) # None return inner f = wraaper() f() name = '番薯' def wraaper(n): n = '番薯' def inner(): print(n) print(inner.__closure__) # cell at 0x000002AD93BF76D8 inner() return inner wraaper(name)
3、闭包的原理
def game(game_name): def hero(name): print("%s: %s" % (game_name, name)) return hero my_game = game("英雄联盟") print(dir(my_game)) print(my_game.__closure__) print(type(my_game.__closure__[0])) print(my_game.__closure__[0].cell_contents)
通过__closure__属性看到,它对应了一个tuple,tuple的内部包含了cell类型的对象。
对于这个例子,可以得到cell的值(内容)为"英雄联盟",也就是变量"game_name"的值
原理解释: 当内嵌(hero)函数引用了包含它的函数(game)中的变量(game_name)后, 这个变量会被保存在(hero)的__closure__的cell对象的属性中,成为(hero)本身的一部分; 也就是说,这些变量的生命周期会和(hero)一样。 在上面例子中就是:my_game变量没被销毁,那么game_name("英雄联盟")这个变量会一直存在于my_game变量的属性中
'''
闭包作用:
当程序执行时,遇到了函数执行,他会在内存中开辟一个空间,局部名称空间,
如果这个函数内部形成了闭包,
那么它就不会随着函数的结束而消失。
'''
什么时候用到闭包?
例如:爬虫,装饰器等
下面是一个爬虫的小案例:
from urllib.request import urlopen def index(url): def parse(type): if type == "GET": return urlopen(url).read() else: return # 另外的处理,自己写逻辑即可 return parse xiaohua = index(url="http://www.xiaohua100.cn/index.html") get_content = xiaohua("GET") print(get_content.decode('utf-8')) post_content = xiaohua("POST")
三、可迭代对象
可迭代的对象(数据类型)如下: 在python中,但凡带内置有__iter__()方法的数据类型(或者对象),都是可迭代的对象
1.列表类型
2.元组类型
3.集合类型
4.字典类型
5.文本类型(文本类型本身就是迭代器对象"具有__next__()"方法)
不可迭代的对象(数据类型)如下:
1.整数类型
2.浮点数类型
for i in 'abc': print(i) for i in 123: print(i) # 'int' object is not iterable,表示不是可迭代对象 # dir():返回一个列表,列表里面包含了传入的参数的属性、方法 dir('abc') # ['__add__', '__class__', '__contains__', '__iter__', __str__' ...]
判断一个对象是否是可迭代对象:
# 第一个方法 s1 = 'abc' dic = {'name':'xiaoming'} print('__iter__' in dir(s1)) print('__iter__' in dir(dic)) # 第二种方法 from collections import Iterable # 判断是否为可迭代对象 from collections import Iterator # 判断是否为迭代器 print(isinstance('xiaoming',Iterable)) # True print(isinstance('xiaoming',Iterator)) # False print(isinstance('xiaoming',str)) # True #isinstance() 应用比type()更广泛,isinstance()不仅可以判断你是不是可迭代对象, #也可判断你是否迭代器,还可以判断你是什么类型的数据等等,而#type()只能判断你是什么数据类型
四、迭代器
1、对象内部含有__iter__方法且含有__next__方法就是迭代器
#文件句柄是迭代器: f = open('register', encoding='utf-8') print('__iter__' in dir(f)) #True print('__next__' in dir(f)) #True #字典是可迭代对象,不是迭代器。 print('__iter__' in dir(dict)) #True print('__next__' in dir(dict)) #False
2、可迭代对象与迭代器的区别:
- 可迭代对象的取值依赖索引
- 迭代器提供了一种不依赖索引取值的方式
- 迭代器非常节省内存
- 迭代器每次只会取一个值
- 迭代器单向的,一条路走到头
- 迭代器一定是可迭代对象,反之却不一定
3、可迭代对象如何转化成迭代器
可以使用函数 iter() 或者 __iter__() 获取相应的迭代器
lis = [1, 2, 3] # 可迭代对象 ite1 = lis.__iter__() # 迭代器 <list_iterator object at 0x0000027A183BFFD0> ite1 = iter(lis) # 迭代器 <list_iterator object at 0x0000027A183BFFD0> print(ite1)
4、迭代器如何取值?
使用next() 或者 __next__() ,每使用一次next 取一个值
print(ite1.__next__()) print(ite1.__next__()) print(next(ite1))
5、while循环模拟for循环机制
1,将可迭代对象转化成迭代器。
2,调用__next__方法取值。
3,利用异常处理停止报错。
s1 = 'abcdefg' iter1 = s1.__iter__() while 1: try: print(iter1.__next__()) except StopIteration: break
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix