高级 python 功能:列表生成器、生成器、迭代器。
Python高级特性
- 列表生成式:不过一种语法糖
- 生成器:不过一个方法
- 迭代器:
列表生成式
Python内置的函数,来创建list。
简单的生成:
>>> list(range(1,11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
复杂的生成:增加一个for循环。
>>> a = [x*x for x in range(1, 11)] >>> a [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
还可以在for循环后加if判断,这样对循环后的所有元素进行整除,得到符合条件的一组元素,返回的是list。
>>> a = [x*x for x in range(1, 11) if x%2 == 0] >>> a [4, 16, 36, 64, 100]
还可以套用两层for循环
>>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
for循环可以同时接收多个变量。比如dict的items()方法:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> for k, v in d.items(): ... print(k, '=', v) ... x = A y = B z = C
>>> d.items() dict_items([('x', 'A'), ('y', 'B'), ('z', 'C')])
小结:
list生成式:
[表达式/0~n个变量 for 0~n个变量 in list/dict/相关变体]
本质就是使用循环和条件语句对list/dict进行筛选:
[表达式/变量 条件/循环语句 条件/循环语句]
Ruby类似的方法糖。
Ruby没有列表生成式,但有很多语法糖,来操作array对象。
range对象使用to_a转化为数组。
> (1...11).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
乘方后,增加一个判断:等同Python上面的列表生成式????。
> (1...11).to_a.map{|x| x * x}.select {|x| x % 2== 0}
=> [4, 16, 36, 64, 100]
Ruby的each功能很强大:可以替代上面for接收多个变量的情况。
Ruby的for..in..功能同样可以做到上面Python的功能:⚠️还更简便,无需调用items(),
> for k,v in d > puts "#{k}=#{v}" > end x=A y=B z=C
Python测试:
请修改列表生成式,通过添加if
语句保证列表生成式能正确地执行:
L1 = ['Hello', 'World', 18, 'Apple', None] L2 = [x.lower() for x in L1 if isinstance(x, str)] >>> L2 >['hello', 'world', 'apple']
解释: 就是简化写法,没别的。 经常用一看就直到。太复杂的还是分开写,便于后续维护代码。毕竟视觉上,分层容易理解。
L1 = ['Hello', 'World', 18, 'Apple', None] L2 = [] for x in L1: if isinstance(x, str): L2.append(x.lower())
Generator (详情见PEP 255 -- Simple Generators)
参考:
三分钟看懂什么是Python生成器
生成器就是一个在行为上和迭代器非常类似的对象,如果把迭代器比作 Android 系统,那么生成器就是 iOS,二者功能上差不多,但是生成器更优雅。
什么是迭代器:
迭代器就是用于迭代操作(for 循环)的对象,它像列表一样可以迭代获取其中的每一个元素,任何实现了 __next__ 方法 (python2 是 next)的对象都可以称为迭代器。
它与列表的区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素,这正是它的优点。
比如列表含有中一千万个整数,需要占超过400M的内存,而迭代器只需要几十个字节的空间。因为它并没有把所有元素装载到内存中,而是等到调用 next 方法时候才返回该元素(按需调用 call by need 的方式,本质上 for 循环就是不断地调用迭代器的next方法)
什么是生成器
普通函数用 return 返回一个值,和ruby等其他语言一样。然而在 Python 中还有一种函数,用关键字 yield 来返回值,这种函数叫生成器函数。
函数被调用时会返回一个生成器对象,生成器本质上还是一个迭代器,也是用在迭代操作中,因此它有和迭代器一样的特性,唯一的区别在于实现方式上不一样,后者更加简洁。
>>> def func(n): ... yield n*n ... >>> func <function func at 0x1042aac10> >>> g = func(10) >>> g <generator object func at 0x1042b4510> >>> next(g) 100 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
- func就是一个生成器函数。<function func at 0x1042aac10>
- 调用它时返回的就是生成器<generator object func at 0x1042b44a0>。这个生成器可以用在循环等场景中。
- 可以使用next()函数,把生成器当成参数。这表示执行一次生成器,遇到yield返回。
这种一边循环一边计算的机制,称为生成器:generator。
- 第一种方法很简单,只要把一个列表生成式的
[]
改成()
,就创建了一个generator。 - 第二种方法,如果一个函数定义中包含
yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator。
除了使用next(), 还可以使用for循环,它的本质也是调用next() , 例子:
def _odd_iter(n): while True: n += 2 yield n a = _odd_iter(1) #因为生成器generator也是可迭代对象,所以可以使用for..in..循环 for i in a: print(i) if i > 10: # _odd_iter自身没有退出代码,所以这里加一个判断用于退出循环。 break
为什么使用生成器?
生成器其实就是迭代器的高级版本,一种用普通的函数语法定义的迭代器。更好用。
我的理解生成器就是迭代器的一大块语法糖。省掉了自己创建迭代器的时间。
用Generator来生成一个杨辉三角函数
1
/ \
1 1
/ \ / \
1 2 1
/ \ / \ / \
1 3 3 1
/ \ / \ / \ / \
1 4 6 4 1
/ \ / \ / \ / \ / \
1 5 10 10 5 1
把每一行看做一个list,试写一个generator,不断输出下一行的list:
# 杨辉三角的基本性质:每个数字等于上一行的左右两个数字之和。 # C(n+1,i) = c(n, i) + c(n, i -1) # def triangles(high): # # 得到一个list。内有high个list元素。每个元素记录一行的数字。 # # 用triangle来储存杨辉三角的数据。 # triangle = [] # z = 1 # while z <= high: # triangle.append([]) # z += 1 # # i = 2 #行号。注意不是数组号。 # s = 1 #一个过渡变量。代表一个位置的数字的大小。 # # print("1\n") #第一行的1 # triangle[0].append(1) # while i <= high: #从第2行开始循环,到high # # print("1") #本行第一个1 # triangle[i -1].append(1) # # j = 1 #j是列位置 # while j <= i - 2: #计算中间的数字。 # # 根据杨辉三角自身的性质:设当前行为i, 要求得的数字的左侧数字的位置是j并且大小是s。 # # 那么,要求的数字 = (i-j)*s/j。 # s = (i-j)*s/j # # print("%d" %s) # triangle[i-1].append(int(s)) # j += 1 # # print("1\n") #本行最后一个1,换行 # triangle[i-1].append(1) # i += 1 # s = 1 # # return triangle # 把上面的函数,转化为generator: def triangles(): # 没有参数,high。执行一次返回一行数字。 # 用triangle来储存杨辉三角的数据。 triangle = [[]] i = 2 #行号。注意不是数组号。 s = 1 #一个过渡变量。代表一个位置的数字的大小。 # print("1\n") #第一行的1 triangle[0].append(1) yield triangle[0] while i : #从第2行开始循环 triangle.append([]) #l必须添加一个内部list用于放置本行数字。 # print("1") #本行第一个1 triangle[i -1].append(1) j = 1 #j是列位置 while j <= i - 2: #计算中间的数字。 # 根据杨辉三角自身的性质:设当前行为i, 要求得的数字的左侧数字的位置是j并且大小是s。 # 那么,要求的数字 = (i-j)*s/j。 s = (i-j)*s/j # print("%d" %s) triangle[i-1].append(int(s)) j += 1 # print("1\n") #本行最后一个1,换行 triangle[i-1].append(1) yield triangle[i -1] i += 1 s = 1 # 期待输出: # [1] # [1, 1] # [1, 2, 1] # [1, 3, 3, 1] # [1, 4, 6, 4, 1] # [1, 5, 10, 10, 5, 1] # [1, 6, 15, 20, 15, 6, 1] # [1, 7, 21, 35, 35, 21, 7, 1] # [1, 8, 28, 56, 70, 56, 28, 8, 1] # [1, 9, 36, 84, 126, 126, 84, 36, 9, 1] n = 0 results = [] for t in triangles(): results.append(t) n = n + 1 if n == 10: break for t in results: print(t) if results == [ [1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1], [1, 6, 15, 20, 15, 6, 1], [1, 7, 21, 35, 35, 21, 7, 1], [1, 8, 28, 56, 70, 56, 28, 8, 1], [1, 9, 36, 84, 126, 126, 84, 36, 9, 1] ]: print('测试通过!') else: print('测试失败!')
还有更简单的方法,都是通过对杨辉三角的特性来写出代码的
下面的例子使用的就是杨辉三角的基本特性:
一个位置的值 = 上一行相同位置的值+ 这个值的左相邻的值
C(n+1,i) = c(n, i) + c(n, i -1) #i代表列位置。
# def triangles(): # t = [1] # yield t # while True: # t = [0] + t + [0] # # new_t = [] # # range(1, 3),会输出1,2。 # for i in range(len(t)-1): # #len(t) -1 表示最后一个元素。但range(len(t)-1),的范围,不包括最后一个元素。 # new_t.append(t[i] + t[i + 1]) # t = new_t # yield t
# 上面的代码还可以使用list生成式简化: def triangles(): t = [1] while True: yield t t = [0] + t + [0] t = [t[i] + t[i + 1] for i in range(len(t) -1)]
参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128#0
百度知道:杨辉三角
小结:
在Python中,可以简单地把列表生成式改成generator,也可以通过函数实现复杂逻辑的generator。
对于函数改成的generator来说,遇到return
语句或者执行到函数体最后一行语句,就是结束generator的指令,for
循环随之结束。
Iterator迭代器。
集合数据类型,如list
、tuple
、dict
、set
、str.
generator:指的是
生成器<generator object func at 0x1042b44a0>和生成器函数<function func at 0x1042aac10>。
上面的数据类型都可以直接用于for循环。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
内部函数isinstance():可以判断一个对象,是否符合某个数据类型。
>>> isinstance('abc', Iterable) True
>>> isinstance("123", str)
True
>>> isinstance(1, int)
True
>>> isinstance(1.1, float)
True
>>> isinstance({"a": 10}, dict)
True
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
>> from collections.abc import Iterator >>> isinstance((x for x in range(10)), Iterator) True
Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数.
上一篇: Python 的列表生成器、生成器、可迭代对象和迭代器
下一篇: Python 的列表生成器