- 字面意思:python
- 对象:在Python中一切皆对象。就是一个实实在在的值。git
- 可迭代:更新迭代,重复的,循环的一个过程,更新迭代每次都有新的内容。api
- 可迭代对象: 能够进行循环更新的一个实实在在的值。安全
- 专业角度:可迭代对象就是内部含有__iter__ 方法的对象。闭包
str1 = "hello " print(dir(str1)) # 输出结果: ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
'__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find',
'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier',
'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition',
'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
st1 = "hello" print("__iter__" in dir(st1)) # True lis = [11,22,33] print("__iter__" in dir(lis)) # True
- 字面意思:能够进行循环更新的一个实实在在的值。app
- 专业角度:内部还有__iter__ 方法的对象就是可迭代对象。ssh
- 判断一个对象是否是可迭代对象: ide
__iter__ in dir(对象);
- 优势:函数
1. 存储的数据能显示,比较直观。工具
2. 拥有的方法比较多,操做方便。
- 缺点:
1. 占用内存
2. 不能直接经过for循环吗,不能直接取值(索引,key)
- 字面意思: 可更新迭代的工具
- 专业角度:内部还有__iter__方法而且含有__next__ 方法的对象就是迭代器
- 判断是不是迭代器:
'__iter__' and '__next__' in dir(对象)
with open("a.txt",mode="w",encoding="utf-8") as f: print(("__iter__"in dir(f)) and ("__next__"in dir(f))) # True
lis = [11,22,]
iter(lis)
lis = [11,22,33,44,55,66,77,88,99,14,12,15] obj = iter(lis) # 将可迭代对象转化成迭代器 while True: try: print(next(obj)) except StopItertion: break
- 字面意思:可更新迭代的工具
- 专业角度:内部还有__iter__ 方法而且含有__next__方法的对象就是迭代器
优势:
1.节省内存
2. 惰性机制,next一次,取一次值
缺点:
1.速度慢
2.不走回头路
1. 可迭代对象是一个操做方法比较多,比较直观,存储数据相对少(几百万个对象,8G内存是能够承受的)的一个数据集。 2. 当你侧重于对于数据能够灵活处理,而且内存空间足够,将数据集设置为可迭代对象是明确的选择。 3. 迭代器是一个很是节省内存,能够记录取值位置,能够直接经过循环+next方法取值,可是不直观,操做方法比较单一的数据集。 4. 当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。
什么是生成器?这个概念比较模糊,各类文献都有不一样的理解,可是核心基本相同。生成器的本质就是迭代器,在python社区中,大多数时候都把迭代器和生成器是作同一个概念。不是相同么?为何还要建立生成器?生成器和迭代器也有不一样,惟一的不一样就是:迭代器都是Python给你提供的已经写好的工具或者经过数据转化得来的,(好比文件句柄,iter([1,2,3])。生成器是须要咱们本身用python代码构建的工具。最大的区别也就如此了。
1.生成器函数
2. 生成器推导式
3. Python内部提供的(内置函数或者模块)
咱们先来研究经过生成器函数构建生成器
首先,咱们先看一个简单的函数
def func(): print(11) return 22 ret = func() print(ret) # 运行结果: 11 22
将函数中的return换成yield,这样func就不是函数了,而是一个生成器函数
def func(): print(11) yield 22
咱们这样写没有任何的变化,这是为何呢? 咱们来看看函数名加括号获取到的是什么?
def func(): print(11) yield 22 ret = func() print(ret) # 运行结果: <generator object func at 0x000001A575163888>
运行结果为何不同呢?因为函数中存在yield,那么这个函数就是一个生成器函数.
当咱们再次执行这个函数的时候,就再也不是函数的执行了,而是获取这个生成器对象,那么生成器对象如何取值呢?
以前咱们说了,生成器的本质就是迭代器.迭代器如何取值,生成器就如何取值。因此咱们能够直接执行next()来执行如下生成器
def func(): print("111") yield 222 gener = func() # 这个时候函数不会执⾏. ⽽是获取到⽣成器 ret = gener.__next__() # 这个时候函数才会执⾏ print(ret) # 而且yield会将func生产出来的数据 222 给了 ret。 # 执行结果: 111 222
生成器函数中能够写多个yield。
def func(): print(123) print(666) yield 222 a = 1 b = 2 c = a + b print(c) yield 88 ret = func() print(next(ret)) print(next(ret)) # 执行结果 # 123 # 666 # 222 # 3 # 88 # 一个next 对应一个yield
yield和return 的区别:
return:通常在函数中只能设置一个,它的做用是终止函数,而且把返回值给函数的调用者。
yield:只要函数中有yield,那么就是生成器函数。生成器函数中能够存在多个yield,yield不会终止函数,一个yield对应一个next。
举例:吃包子练习
咱们来看一下这个需求:向楼下卖包子的老板订购了1000个包子.包子铺老板很是实在,一下就所有都作出来了
def eat(): lst = [] for i in range(1, 1000): lst.append('包子' + str(i)) return lst e = eat() print(e)
这样作没有问题,只吃了200个左右,剩下的800个,就只能占着必定的空间,放在一边了。若是包子铺老板效率够高,我吃一个包子,你作一个包子,那么这就不会占用太多空间存储了,完美。
def eat(): for i in range(1,1000): yield '包子'+str(i) e = eat() for i in range(200): next(e)
这二者的区别:
第一种是直接把包子所有作出来,占用内存。
第二种是吃一个生产一个,很是的节省内存,并且还能够保留上次的位置。
def eat(): for i in range(1,10000): yield '包子'+str(i) e = eat() for i in range(200): next(e) for i in range(300): next(e) # 屡次next包子的号码是按照顺序记录的。
在python3中提供一种能够直接把可迭代对象中的每个数据做为生成器的结果进行返回
# 对比yield和yield from的区别 def func(): lis = [1,2,3,4,5] yield lis ret = func() print(next(ret)) # 返回一个列表 # 执行结果: # [1,2,3,4,5]
def func(): l1 = [1, 2, 3, 4, 5] yield from l1 ret = func() print(ret) # 他会将这个可迭代对象(列表)的每一个元素当成迭代器的每一个结果进行返回。 for i in range(5): print(next(ret)) # 执行结果 # 1 # 2 # 3 # 4 # 5
有个小坑,yield from 是将列表中的每个元素返回,因此 若是写两个yield from 并不会产生交替的效果
def func(): l1 = [1, 2, 3, ] lis1 = [7, 8, 9] yield from l1 yield from lis1 ret = func() print(ret) for i in range(6): print(next(ret)) # 执行结果: 1 2 3 7 8 9
从更深层的角度去理解yield from 有两个做用
1. 他能够彻底代替内存循环,提升效率,让代码读起来更加顺畅
2. 还能够建立通道,把内存生成器直接与外层生成器的客服端链接起来
1. 闭包只能存在嵌套函数中
2. 内层函数对外层函数非全局变量的引用,就会造成闭包
3. 被引用的非全局变量也称自由变量,这个自由变量与内层函数产生一个绑定关系。
4. 自由变量不会再内存中消失
保证局部信息不被销毁,保证数据的安全性。
如何判断一个嵌套函数是否是闭包
1.闭包只能存在嵌套函数中
2. 内层函数对外层函数非全局变量的引用(使用),就会造成闭包。
def func(): a = 1 def inner(): print(a) return inner ret = func() # 是闭包 a = 2 def func(): def inner(): print(a) return inner ret = func() # 不是闭包,a是全局变量 def func(a,b): def inner(): print(a) print(b) return inner a = 2 b = 3 ret = func(a,b) print(ret()) 就至关于这样 def func(a,b): a = 2 b = 3 def inner(): print(a) print(b) return inner ret = func(a,b) print(ret())
def func(a,b): def inner(): print(a) print(b) return inner a = 2 b = 3 ret = func(a,b) print(ret.__code__.co_freevars)
# ('a', 'b')