一、列表生成式(List Comprehensions)

1、案例——列表每个值加1

  先有列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求你把列表里的每个值加1,怎么实现?

方法一:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = []
for i in a:b.append(i+1)
a = b
print(a)

  此方法内存中会同时有两份列表,因此不适合处理大规模数据。

方法二:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for index,i in enumerate(a):
a[index] +=1
print(a)

方法三:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = map(lambda x:x+1, a) # <map object at 0x1018da5f8>
print(a)

  利用map方法根据提供的函数(lambda)对指定序列做映射。

方法四:

# 列表生成式:一行代码完成列表操作
a = [i+1 for i in range(10)]

2、列表生成式概念和用法

  列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

# 写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] # for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100] # 用三元运算生成列表
>>> a = range(1,11)
>>> a = [i if i < 5 else i*i for i in a]
>>> a
[1, 2, 3, 4, 25, 36, 49, 64, 81, 100] # 使用两层循环,可以生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] # 列表生成式也可以使用两个变量来生成list
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['x=A', 'y=B', 'z=C'] # 字符串操作,都变为小写
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

  由此可以看到,通过列表生成式可以直接创建一个列表。列表创建在内存中,因此列表容量受到内存限制。特别是对一个元素量很大的列表,仅需访问前几个元素时,尤其浪费空间。

二、生成器(generator)

1、生成器定义

  列表元素可以按照某种算法推算出来(有规律的数组),则可以在循环的过程中不断推算出后续的元素。这种方式就不必创建完整的list,可以节省大量的空间。

  python中,这种一边循环一边计算的机制,称为生成器:generator。

2、生成器创建方法一:将列表生成式的‘[]’改为‘()’

  创建列表生成式和生成器的区别,仅仅是最外层的[]和()。

  如果要打印出生成器的元素,可以通过nex()函数获取generator的下一个返回值。

# 生成器保存的是公式,取一次创建一次,只能往前不能后退
>>> a2 = (i for i in range(1000))
>>> a2
<generator object <genexpr> at 0x103761a98>
>>> next(a2)
0
>>> next(a2)
1 #生成器走完时,会报错:StopIteration
>>> a3 = (i for i in range(5)) # 限制5个
>>> a3
<generator object <genexpr> at 0x103761e08>
>>> next(a3)
0
>>> next(a3)
1
>>> next(a3)
2
>>> next(a3)
3
>>> next(a3)
4
>>> next(a3)
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration

  生成器保存的是算法,每次调用next(g)就计算出g的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误。

  创建生成器后,很少会调用next(),一般是通过for循环来迭代

a3 = (i for i in range(5))
for i in a3: # 使用for循环来迭代生成器,不会出现StopIteration报错,直接结束。
print(i)
'''
0
1
2
3
4
''' a2 = (i for i in range(3))
while True: # while循环不适用
next(a2) # 报StopIteration错误

3、生成器创建方法二:一个函数定义中包含yield关键字,函数为生成器(generator)

  例如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数均可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34,....

  列表生成式写不出斐波拉契数列,但是函数却可以轻松打印:

# 用函数来写生成器,以斐波那契数列为例
# 重点是赋值语句:a,b = b,a+b
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b) # 每次打印b
a, b = b, a + b # 0,1——>1,1——>1,2——>2,3
n = n + 1 # n用来计数,每次自加1
return 'done' fib(10)

  赋值语句:a, b = b , a + b

  相当于:t = (b, a + b)   a = t[0]   b = t[1]   t是一个tuple

  fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

>>> def fib_g(max):
... n, a, b = 0, 0, 1
... while n < max:
... print('before yield')
... yield b # yield 把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
... print(b)
... a, b = b, a+b
... n = n + 1
... return 'done'
...
>>> f = fib_g(15) # 将函数转换为生成器,有了yeild后,函数名(参数)根本不执行
>>> next(f)
before yield
1
>>> next(f)
1
before yield
1
>>> next(f)
1
before yield
2
>>> next(f)
2
before yield
3
>>> next(f)
3
before yield
5

  这种生成器和函数相似,但与函数的执行流程不同:

  函数是顺序执行,遇到return语句或最后一行函数语句就返回。

  函数转化为生成器后,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

def fib_g(max):
n, a, b = 0, 0, 1
while n < max:
# print('before yield')
yield b # yield 把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
# print(b)
a, b = b, a+b
n = n + 1
return 'done'
# 生成器使用意义:可以将函数执行中的状态、数据返回到外部来 data = fib_g(10)
print(data) print(data.__next__())
print(data.__next__())
print("干点别的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__()) """
<generator object fib_g at 0x1014670f8>
1
1
干点别的事
2
3
5
8 """

  在上例中,循环过程中不断调用yield,就会不断中断。而且需要设置循环退出条件,否则会产生无限数列。

  将函数改写为生成器后,通常不会用next()来获取下一个值,而是使用for循环来迭代。

def fib_g(max):
n, a, b = 0, 0, 1
while n < max:
yield b # yield 把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
a, b = b, a+b
n = n + 1
return 'done' for n in fib_g(6):
print(n) """
1
1
2
3
5
8
"""

  for循环调用生成器,拿不到生成器的return语句的返回值,要拿到返回值,必须捕获StopIteration错误。

def fib_g(max):
n, a, b = 0, 0, 1
while n < max:
yield b # yield 把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
a, b = b, a+b
n = n + 1
return 'done' g = fib_g(6)
while True:
try:
x = next(g)
print('g', x)
except StopIteration as e:
print('Generator return value:', e.value)
break """
g 1
g 1
g 2
g 3
g 5
g 8
Generator return value: done
"""

4、生成器send方法

send方法作用:
  1、唤醒并继续执行  (next方法只有这个功能)
  2、发送一个信息到生成器内部

def range2(n):

    count = 0
while count < n:
print('count', count)
count += 1
sign = yield count
if sign == 'stop':
break
print("---sign", sign)
return 3333 new_range = range2(3) # 0,1,2
n1 = next(new_range)
print(new_range)
new_range.send("stop") # 生成器不再执行后两步直接终止 """
count 0
<generator object range2 at 0x10244c0f8>
Traceback (most recent call last):
File "/Users/.../函数生成器2.py", line 20, in <module>
new_range.send("stop")
StopIteration: 3333
"""

  如下所示为生成器与外部交互:

def run():
count = 0
while True:
n = yield count
print("--", n, count)
count += 1 g =run() g.__next__()
g.send("alex")
g.send("egon")
g.send("jack")
"""
-- alex 0
-- egon 1
-- jack 2
"""

5、通过yield实现单线程情况下实现并发运算的效果

#_*_coding:utf-8_*_
__author__ = 'Alex Li' import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("老子开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2个包子!")
c.send(i)
c2.send(i) producer("alex") 通过生成器实现协程并行运算

三、生成器总结

1、生成器的创建方式   

  1.列表 列表生成式
  2.函数 函数生成器 yield

2、yield 对比 return

  return返回并中止function
  yield返回函数,并冻结当前的执行过程
  函数有了yield之后
  (1)函数名加()就变成了一个生成器
  (2)return在生成器里,代表生成器的中止,直接报错

3、next:唤醒生成器并继续执行

  next唤醒冻结的函数执行过程,继续执行,直到遇到下一个yield

4、send("stop"):

  1.唤醒并继续执行
  2.发送一个信息到生成器内部

5、python2和python3中range()方法对比

在Python2中:
  range = list

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

  xrange = 生成器

>>> a = xrange(10)
>>> type(a)
<type 'xrange'>
>>> for i in a:
... print i
...
0
1
2
3
4
5
6
7
8
9

在Python3中:
  range = genetator

>>> range(10)  # range(0,10)  生成这个公式没有正式创建
range(0, 10)
>>> type(range(10))
<class 'range'>

  xrange :python3中已取消该方法

四、可迭代对象(Iterable)和迭代器(Iterator)

1、什么是可迭代对象?什么是迭代器?

  可直接作用于for循环的数据类型有一下几种:

  一、集合数据类型,如:list、tuple、dict、set、str等;

  二、generator,包括生成器表达式(geneator expression)和生成器函数(generator function)两组构建方式。

  上述这些可以直接作用于for循环的对象统称为可迭代对象(Iterable)。可以使用isinstance()判断一个对象是否是Iterable对象。

from collections import Iterable  # 可迭代类型
# 使用isinstance()判断一个对象是否是可迭代对象
isinstance('abc', Iterable)
isinstance({}, Iterable)
isinstance((x for x in range(10)), Iterable) isinstance(100, Iterable) # 返回False

  迭代器定义:可以被next()函数调用并不断返回下一个值得对象称为迭代器(Iterator)

2、可迭代对象与迭代器的关系

生成器是迭代器的一种:

  1、生成器都是迭代器对象,但list\dict\str虽然是可迭代对象,但不是迭代器。

  2、把list\dict\str等可迭代对象变成迭代器可以使用iter()函数

>>> from collections import Iterator
>>> isinstance('abc', Iterator)
False
>>> a = iter('abc')
>>> a
<str_iterator object at 0x10c584b38>
>>> a.__next__()
'a'
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

3、为什么listdictstr等数据类型不是Iterator

  这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

  Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

4、总结

  凡是可作用于for循环的对象都是Iterable类型;

  凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

  集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

  Python的for循环本质上就是通过不断调用next()函数实现的。 

for x in [1, 2, 4, 5]:
pass # 完全等价于
# 获得Iterataor对象
it = iter([1, 2, 3, 4, 5])
# 循环
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break

列表生成式、生成器&迭代器的更多相关文章

  1. Python之旅Day5 列表生成式 生成器 迭代器 装饰器

    装饰器 器即函数,装饰即修饰,意指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 装饰器涉及的知识点= 高阶函数+函数嵌套+闭包 在遵循下面两个原则的前提下为被装饰者新功能 ...

  2. 列表生成式 生成器 迭代器 yield

    列表生成式 格式:通过一个或者若干个在List里边的for构建List而非List外部的for循环 举个例子:计算从1到10整数的平方构成一个List L=[ x*x for x in range(1 ...

  3. python协程函数应用 列表生成式 生成器表达式

    协程函数应用 列表生成式 生成器表达式   一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._n ...

  4. python 基础 4.4 生成式 生成器 迭代器

    一.生成式和生成器   列表生成式是python受欢迎的语法之一,通过一句简洁的语法就可以对一组元素进行过滤,还可以对得到的元素进行转换处理.   #/usr/bin/python #coding=u ...

  5. 列表生成式,迭代器&生成器

    python3中range(10)就 是迭代器 列表生成式 #列表生成式 a=[0,1,2,3,4,5] b=[] for index,i in enumerate(a): a[index]+1 pr ...

  6. Day4- Python基础4 深浅拷贝、三目运算、列表生成式,迭代器&生成器、装饰器

    本节内容: 1.深浅拷贝 2.三目运算 3.迭代器和生成器 4.装饰器 1.深浅拷贝 拷贝意味着对数据重新复制一份,深浅拷贝的含义就是:对于修改复制的数据是否会影响到源数据,拷贝操作对于基本数据结构需 ...

  7. s14 第4天 关于python3.0编码 函数式编程 装饰器 列表生成式 生成器 内置方法

    python3 编码默认为unicode,unicode和utf-8都是默认支持中文的. 如果要python3的编码改为utf-8,则或者在一开始就声明全局使用utf-8 #_*_coding:utf ...

  8. Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式

    一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._next_() 取下一个值 优点: 1.提供了 ...

  9. python 基础 列表生成式 生成器

    列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式 举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, ...

  10. 列表生成式&生成器表达式

    一.列表生成式 def func(): return [lambda x:i*x for i in range(4)] print([j(2) for j in func()]) 结果:[6,6,6, ...

随机推荐

  1. dll和lib(包括静态链接库和与dll同时生成的lib)

    转:http://blog.csdn.net/galaxy_li/article/details/7411956 1:神马是Dll和Lib,神马是静态链接和动态链接 大家都懂的,DLL就是动态链接库, ...

  2. python高阶函数式编程

    from functools import reduce def str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return ...

  3. linux分析apache日志获取最多访问的前10个IP

    apache日志分析可以获得很多有用的信息,现在来试试最基本的,获取最多访问的前10个IP地址及访问次数. 既然是统计,那么awk是必不可少的,好用而高效. 命令如下: awk '{a[$1] += ...

  4. LinkedHashMap概述

    1. LinkedHashMap概述: LinkedHashMap是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap. LinkedH ...

  5. 安装VMware Workstation提示the msi failed的解决办法

    有朋友安装VMware Workstation时出现报错,提示the msi failed等信息,原来他以前安装过绿色版.优化版的VM,但删掉后重装VM就会有这样的报错提示,如果你也遇到了相同的困扰, ...

  6. gm8180:arm linux启动加载模块、运行程序

    1. init #!/bin/busybox ash#load modules mao 2013-02-16 14:12:48 echo "************************m ...

  7. FFMPEG 实现 YUV,RGB各种图像原始数据之间的转换(swscale)

    FFMPEG中的swscale提供了视频原始数据(YUV420,YUV422,YUV444,RGB24...)之间的转换,分辨率变换等操作,使用起来十分方便,在这里记录一下它的用法. swscale主 ...

  8. Linux忘记开机密码怎么办?

    Linux忘记开机密码怎么办?1. 开机ESC/Shift,在出现grub画面时,用上下键选中你平时启动linux的那一项,然后按e键2. 再次用上下键选中你平时启动linux的那一项(类似于kern ...

  9. Struts2实现文件上传(一)

    Struts2实现文件上传 文件上传成功后结果页面 result.jsp: <%@ page language="java" import="java.util.* ...

  10. iOS - Core Animation 核心动画

    1.UIView 动画 具体讲解见 iOS - UIView 动画 2.UIImageView 动画 具体讲解见 iOS - UIImageView 动画 3.CADisplayLink 定时器 具体 ...