Python高级特性-迭代(Iteration)-列表生成式-生成器

本文介绍了Python中的迭代概念,包括对列表、字典、字符串等的迭代,并讲解了列表生成式及其用法。此外,文章深入探讨了生成器,阐述了它们如何节省内存,并通过实例展示了生成器表达式和自定义生成器函数的实现。最后,通过斐波拉契数列的例子解释了生成器在处理复杂算法时的优势。

迭代


定义

for循环对list或tuple进行遍历我们称之为迭代(Iteration)

  1. 只要属于list类型的对象都可以被迭代.
  2. Python的for循环抽象程度要比Java的高,迭代不仅可以用在list或tuple上还可以用在其他迭代对象上.比如dict,str.
>>> d = {'a':1,'b':2,'c':3}
>>> for x in d:
...     print x
... 
a
c
b

>>> str = 'abc'
>>> for s in str:
...    print s
... 
a
b
c

dict是无序的所以输出结果也无序.dict默认迭代的是key,如果要迭代value呢?可以使用for value in d.itervalues(),如果需要同时迭代key和value可以用for k, v in d.iteritems().

  1. 但是如何判断一个对象是否是可迭代对象呢?我们可以通过collection模块里的Iterable类进行判断.
>>> from collections import Iterable
>>> isinstance('abc',Iterable)
True
>>> isinstance((1,2,3),Iterable)
True
>>> isinstance([1,2,3],Iterable)
True
>>> isinstance(123,Iterable)
False
  1. for可以同时迭代两个变量
>>> for x,y in ((1,1),(2,2),(3,3)):
...     print x ,y 
... 
1 1
2 2
3 3

开拓思维要是需要迭代2个以上的对象呢?

>>> for a,b,c in [(1,1,1),(2,2,2),(3,3,3)]:
...     print a,b,c
... 
1 1 1
2 2 2
3 3 3

列表生成式


定义

列表生成式是Python内置的非常强大的可以用来创建list的生成式
例:生成[1…10]的list

>>> range(1,11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但是如果生成[1x1,2x2,3x3….10x10]的list怎么办?
方法1:循环

>>> for x in range(1,11):
...     y = x * x
...     L.append(y)
... 
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> 

方法2:列表生成式

>>> [x*x for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

x*x代表目标list的元素要放在最前面,后面就是跟for循环取值然后赋值给x带入x*x 进行运算.

生成式的其它用法

  1. for循环后添加if判断
>>> [x*x for x in range(1,11) if x%2 == 0] #筛选出偶数
[4, 16, 36, 64, 100]
>>> [x*x for x in range(1,11) if x%2 != 0] #筛选出奇数
[1, 9, 25, 49, 81]
#只输出符合if语句的内容
  1. 两层循环
>>> [m + n for m in 'abc' for n in '123']
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']

>>> [m + n for m in ['a','b','c'] for n in [1,2,3]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
>>> [m + n for m in ('a','b','c') for n in (1,2,3)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
#只能对同类型的数据进行组合
>>> [m + n for m in ('a','b','c') for n in ('1','2','3')]
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
>>> [m + n for m in ['a','b','c'] for n in ['1','2','3']]
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
>>> [m + n for m in [1,2,3] for n in [4,5,6]]
[5, 6, 7, 6, 7, 8, 7, 8, 9] #以m为基础进行求和,逐次运行.

三层及三层以上的循环基本用不到了.

生成器


定义

所谓生成器就是列表生成器的进化版本:
列表生成器虽然方便创建数组但是也会占用大量内存,如果生成的数组太大,内存就会溢出,有什么好的解决办法呢?生成器(generator)就横空出世了!
生成器会根据数组推算后续数值,一边循环一边计算,这样就不会占用大量空间.
那么问题来了,怎么创建生成器呢?非常简单,把列表生成器的中括号[]改为小括号就可以了()

>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> (x for x in range(10))
<generator object <genexpr> at 0x7fea306baa50> #后面0x..是generator对象的内存地址.

通过上面的代码我们可以看到,生成器不会直接生成列表只是占用一点内存空间,若是需要输出数值有两个方法:一个是直接调用生成器的next()方法;二是通过for循环.
方法1:

>>> g = (x for x in range(10))
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
>>> g.next()
4
>>> g.next()
5
>>> g.next()
6
>>> g.next()
7
>>> g.next()
8
>>> g.next()
9
>>> g.next()
Traceback (most recent call last): #循环完毕跳出error
  File "<stdin>", line 1, in <module>
StopIteration

方法2:

>>> g = (x for x in range(10))
>>> for x in g:
...     print x
... 
0
1
2
3
4
5
6
7
8
9
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
#单个输出数值
>>> g = (x for x in range(10))
>>> l = []
>>> for x in g:
...     l.append(x)
... 
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
# 直接生成list
>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 列表生成器可以重复执行

通过比较方法1和方法2,方法2较方法1更方便,尤其是需要计算的数组比较大的时候.
另外细心的朋友估计能发现,不像列表生成器一样可以重复输出数组.
生成器是一种线性迭代是一次性的不能回头滴.撞了南墙也回不了头啊~只能说句say you error.

更加强大的generator

碰到更复杂的算法的时候,生成器的for循环就不能胜任了,这时候就要请出我们的函数君来解决问题.
以著名的斐波拉契数列(Fibonacci)来举例子吧.
**说明**Fibonacci数列是怎么一回事儿呢? 除了它的第一个和第二个数相等,任意一个数都可以由前面两个数相加得到.
1,1,2,3,5,8,13,21,….
普通文艺函数君:

>>> def fib(n):
...     i,a,b = 0,0,1
...     while i < n:
...         print b # print b 要放在公式的前面执行.
...         a, b = b, a+b
...         i = i + 1
... 
>>> fib(5)
1
1
2
3
5

>>> def fib(n):
...     i,a,b = 0,0,1
...     while i < n:
...         print b
...         a, b = b, a+b
...         i = i + 1
...         print 'new: ',b #添加一个放在后面的
... 
>>> fib(5)
1
new:  1
1
new:  2
2
new:  3
3
new:  5
5
new:  8
# 比较一下输出结果,得出的是(n+1)位的结果.

有可能有一部分同学已经迷糊了~这a , b, a+b,到底咋回事?
其实很好理解,我们先在Fibonacci数列前加个0,

> a     b    a+b # 第一次循环n=0的时候,a,b,a+b相对应的数值
> 0,    1,    1,    2,    3,    5,    8
>       a     b    a+b # 第二次循环n=1的时候,a,b,a+b相对应的数值

通过上面内容我们可以看到a,b,a+b是整体逐次步进的,再去对应下代码,是不是非常好理解~
理解之后我们发现,fib函数也是从第一个元素开始推算后续任意元素的,这个和generator的逻辑非常相似,所以我对普通文艺函数君稍微改造下就可能把他变为逗比generator函数君.
怎么改造呢?只要把fib函数里面的print b改为yield b就可以了!是高富帅还是吊丝就是一步之遥啊.

逗比generator函数君:

>>> def fib(n):
...     i,a,b = 0,0,1
...     while i < n:
...         yield b
...         a, b = b, a+b
...         i = i + 1
... 
>>> fib(5)
<generator object fib at 0x7f29e33c9af0>
# 就是不输出,占坑不拉,你咬我啊.

怎么输出呢?

>>> f = fib(5)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
>>> f.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
# next()方法单个输出
>>> for n in fib(5):
...     print n
... 
1
1
2
3
5
# for循环一次性输出

yieldprint有啥区别呢?可以这么理解yield,只能通过next()for循环来调用,在调用的过程中边运行边计算,当函数在循环过程中碰到yield就会输出一个数值.直到循环结束没有yield了然后返回一个error,当然只有使用next()方法的时候才会返回error,使用for循环的时候没有error返回.

Python 中,**迭代器(Iterator)** 和 **生成器(Generator)** 是两种处理可迭代对象的方式,它们都用于逐项访问集合或数据流,但实现机制和使用场景有所不同。 ### 迭代器(Iterator) 迭代器是一个实现了迭代协议的对象,该协议包括 `__iter__()` 和 `__next__()` 方法。迭代器可以用于遍历集合,如列表、元组、字典和字符串等。当没有更多元素时,调用 `__next__()` 方法会引发 `StopIteration` 异常。 #### 示例代码: ```python # 定义一个简单的迭代器 class MyIterator: def __init__(self, data): self.data = data self.index = 0 def __iter__(self): return self def __next__(self): if self.index >= len(self.data): raise StopIteration value = self.data[self.index] self.index += 1 return value # 使用迭代器 my_iter = MyIterator([1, 2, 3]) print(next(my_iter)) # 输出: 1 print(next(my_iter)) # 输出: 2 try: print(next(my_iter)) # 输出: 3 print(next(my_iter)) # 引发 StopIteration except StopIteration: print("Iteration finished") ``` ### 生成器(Generator) 生成器是一种特殊的迭代器,它通过函数来实现,并使用 `yield` 语句返回数据。生成器函数在每次调用 `next()` 时会记住当前的状态,因此不需要手动维护状态变量。生成器通常用于处理大数据流或无限序列。 #### 示例代码: ```python # 定义一个简单的生成器 def simple_generator(): x, y = 1, 2 yield x, y x += 1 yield x, y # 使用生成器 gen = simple_generator() print(next(gen)) # 输出: (1, 2) print(next(gen)) # 输出: (2, 2) try: print(next(gen)) # 引发 StopIteration except StopIteration: print("Iteration finished") ``` ### 迭代器与生成器的区别 1. **实现方式**: - 迭代器需要手动实现 `__iter__()` 和 `__next__()` 方法。 - 生成器通过函数定义,并使用 `yield` 语句返回数据,状态由 Python 自动管理 [^1]。 2. **内存效率**: - 迭代器通常需要在内存中存储整个数据集。 - 生成器按需生成数据,节省内存,适合处理大数据集或无限序列。 3. **代码简洁性**: - 迭代器代码较为复杂,需要手动管理状态。 - 生成器代码简洁,易于实现。 4. **适用场景**: - 迭代器适用于需要自定义迭代逻辑的场景。 - 生成器适用于需要惰性求值或处理大数据流的场景。 ### 使用方法 - **迭代器**:适用于需要遍历集合或自定义迭代逻辑的场景。 - **生成器**:适用于需要生成大量数据或惰性求值的场景,例如生成无限序列或处理文件流。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值