文章目录

  • Python高级特性
  • 切片
  • 迭代
  • 判断是否可迭代
  • 如何实现下标循环
  • 列表生成式
  • ⭐️生成器
  • 什么是生成器?
  • 为什么要引入生成器?
  • 如何使用生成器?
  • generator和函数的区别
  • 迭代器
  • Iterable与Iterator


Python高级特性

python中提供了切片、迭代、列表生成式、生成器、迭代器等诸多高级特性,多多使用高级特性,可以使得我们的代码更加简单,Python的特点就是代码简单。1行代码能实现的功能,决不写5行代码。

切片

对于取指定索引范围的操作,Python提供了切片(Slice)操作符(用循环十分繁琐)。切片操作对于List、tuple、字符串都同样适用,切片返回的结果仍然是原类型。举几个例子:

操作

写法

说明

取前3个元素

L[0:3]L[:3]

第1个索引是0可以省略

取第i~j个元素

L[i:j+1]

取切片的范围是到n-1

取倒数第1个元素

L[-1]

-1表示最后一个元素

取后10个数

L[-10:]

负号表示倒数,-10自然就表示倒数第10个元素,最后1个索引不写默认为n

前10个数,每两个取一个

L[:10:2]

所有数,每五个取一个

L[::5]

迭代

给定一个listtuple,我们可以通过for循环来遍历这个listtuple,这种遍历我们称为迭代(Iteration)。listtuplestrdict都是可迭代对象。

判断是否可迭代

通过collections.abc模块的Iterable类型可以判断一个对象是否是可迭代对象:

>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False

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

如何实现下标循环

Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

>>> for i, value in enumerate(['A', 'B', 'C']):
...     print(i, value)
...
0 A
1 B
2 C

上面的for循环里,同时引用了两个变量,在Python里是很常见的,比如下面的代码:

>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
...     print(x, y)
...
1 1
2 4
3 9

列表生成式

列表生成式(List Comprehensions),又称列表解析式,是Python内置的非常简单却强大的可以用来创建list的生成式运用列表生成式,可以写出非常简洁的代码

注:其实numpy中的一些内置函数功能更为强大且更加常用。

  • 例子1,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • 例子2,生成仅偶数的平方和List:
    把要生成的元素x*x放到前面,后面可以加if
>>> [x*x for x in range(1,11) if x % 2 == 0]
[4, 16, 36, 64, 100]
  • 还可以加上判断条件if-else,但要加在for的前面,for后面的if是过滤条件,不能带else
    例子3,把L1的字符全部变为小写,剔除非字符串。
>>> L1 = ['Hello', 'World', 18, 'Apple', None]
>>> L2 = [str.lower(x) for x in L1 if isinstance(x, str)]
['hello', 'world', 'apple']

⭐️生成器

生成器(generator)是Python中非常有特点的一个特性,其他主流语言中往往没有这个特性。

什么是生成器?

如果列表元素可以按照某种算法推算出来,我们可以在循环的过程中不断推算出后续的元素。这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

为什么要引入生成器?

说生成器之前,先说说列表解析式,他的优点很多,比如运行速度快、编写简单,但是有一点我们不要忘了,他是一次性生成整个列表。如果整个列表非常大,这对内存也同样会造成很大压力,想要实现内存的节约,可以将列表解析式转换为生成器表达式。

如何使用生成器?

  • 创建生成器
    要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator。创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。
  • 遍历生成器
  1. generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
  2. 我们通常都是通过都是for循环来迭代它,并且不需要关心StopIteration的错误。
>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9
16
25
36
49
64
81
  1. 如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个生成器函数(generator function)
# 输出斐波那契数列
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

generator和函数的区别

generator和函数的执行流程不一样。

函数是顺序执行,每次调用从头执行,遇到return语句或者最后一行函数语句就返回。

而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。直到没有yield可以执行了就报错。

杨辉三角代码:

# -*- coding: utf-8 -*-

def triangles():
    L = [1]
    while True:
        yield L[:]
        L.append(0)
        L = [L[i-1]+L[i] for i in range(len(L))]

n = 0
results = []
for t in triangles():
    results.append(t)
    n = n + 1
    if n == 10:
        break

for t in results:
    print(t)

输出:
[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]

迭代器

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

例如生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

Iterable与Iterator

可以直接作用于for循环的对象统称为可迭代对象:Iterable,如listtupledictsetstr等。**生成器都是Iterator对象,但Iterable却不一定是Iterator。**例如listdictstr虽然是Iterable,却不是Iterator

Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以**Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。**Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

凡是可作用于for循环的对象都是Iterable类型;凡是可作用于next()函数的对象都是Iterator类型。

集合数据类型如listdictstrIterable对象可以通过iter()函数获得一个Iterator对象

迭代器是Python内置的重要特性,Python的for循环本质上就是通过不断调用next()函数实现的,例如:

for x in [1, 2, 3, 4, 5]:
    pass

实际上完全等价于:

# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break