真锋
永远保持一颗学习和专注的心
嵌入式视觉笔记

Python3 高级特性

本文为学习笔记,参考资料《廖雪峰-Python3教程》

目录

  • 切片
  • 迭代
  • 列表生成式
  • 生成器
  • 迭代器

掌握了 Python 的数据类型、语句和函数,基本上就可以编写出很多有用的程序了。但是我们还需要掌握Python的一些高级特性,精简代码。一行代码能实现的功能,绝不写5行代码。Python的高级特性:切片,迭代,列表生成式,生成器,迭代器

切片

对这种经常取指定索引范围的操作,用循环十分繁琐,因此, Python 提供了切片(Slice)操作符,能大大简化这种操作。取前 3 个元素,用一行代码就可以完成切片:

切片实例
切片代码

注意:tuple 也是一种 list,唯一区别是 tuple 不可变。因此, tuple 也可以用切片操作,只是操作的结果仍是 tuple:

迭代

如果给定一个 list 或 tuple,我们可以通过 for 循环来遍历这个 list 或tuple,这种遍历我们称为迭代(Iteration),迭代时 Python 最强大的功能之一,是访问集合元素的一种方式。list 这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标,都可以迭代,比如 dict 就可以迭代: 

迭代实例

因为 dict 的存储不是按照 list 的方式顺序排列,所以,迭代出的结果顺序很可能不一样(key 是无序的)。默认情况下, dict 迭代的是 key。

  • 如果要迭代 value,可以用 for value dict.values()
  • 如果要同时迭代 key 和 value,可以用 for k, v in dict.items()
dict 迭代 key 和 value

想要使用 dict 时,Key 是有序的,可以用 OrderedDict,如下:

from collections import OrderedDict
od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3
list(od.keys())  # 按照插入的 Key 的顺序返回['z', 'y', 'x']

字符串也是可迭代对象,因此,也可以作用于 for 循环: 

迭代字符串

所以,当我们使用 for 循环时,只要作用于一个可迭代对象, for 循环就可以正常运行,而我们不太关心该对象究竟是 list 还是其他数据类型。通过 collections 模块的 Iterable 类型判断一个对象是否可迭代。list,tuple,dict,字符串对象都是可迭代对象。 

注意:Python 内置的 enumerate 函数可以把一个 list 变成索引-元素对,这样就可以在 for 循环中迭代索引和列表元素本身。例子如下:

enumerate函数迭代

列表生成式

列表生成式即 List Comprehensions,是 Python 内置的非常简单却强大的可以用来创建 list 的生成式,可以让代码更简洁好看。比如实际项目中,如果碰到需要改变列表(迭代器)元素值的情况,简单写法是用 for 循环遍历出元素,然后处理,但是当循环体的代码只有一行或者比较简单时,可以考虑用列表生成式的高级写法代替原先的简单写法,让代码更简洁。如以下代码可用列表生成式写法代替:

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

很明显,上述代码的循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的 list。写列表生成式时,把要生成的元素 x * x 放到前面,后面跟 for 循环,就可以把 list 创建出来,代码如下。

列表生成式代码1

对于两层 for 循环的情况左边的循环是外层循环,右边的循环是内层循环,三层和三层以上的循环也等效,不过实际项目中很少用到。

运用列表生成式,可以写出非常简洁的代码。例如,把一个 list 中所有的字符串变成小写,可以通过一行代码实现:

列表生成式实例2

Python列表解析配合if else

  1. [i for i in range(k) if condition]:此时 if 起条件判断作用,满足条件的,将被返回成为最终生成的列表的一员。
  2. [i if condition else exp for exp]:此时 ...if...else... 是三元表达式写法,被用来赋值,满足条件的 if 以及 else 被用来生成最终的列表。

代码示例如下:

print([i for i in range(10) if i%2 == 0])
print([i if i == 0 else 100 for i in range(10)])

[0, 2, 4, 6, 8]
[0, 100, 100, 100, 100, 100, 100, 100, 100, 100]

列表生成式和和 zip 函数结合使用

语法:[expression for x, y in zip(list1, list2)], 列表 list1 和 list2 长度要相等,如果不相等,则按照小的列表长度来遍历元素。示例代码如下:

import numpy as np
list1 = np.arange(0,51)  # 创建序列数组
list2 = np.linspace(0, 100,51, dtype=int)
ret1 = [x1+y1 for x1,y1 in zip(list1, list2)]  # 列表 list1 和 list2 长度相等
print(ret1, len(ret1))

程序运行结果如下:

[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147, 150] 51

小结

运用列表生成式,可以快速生成 list,可以通过一个 list 推导出另一个 list,而代码却十分简洁。 在一个列表生成式中,for 前面的 ...if ... else... 是三元表达式写法,而 for 后面的 if 是过滤条件,不能带 else。注意:列表生成式只能判断最最基础的 if else 语句,不适用于 elif 语句。

>>> [x for x in range(1, 11) if x % 2 == 0]
[2, 4, 6, 8, 10]
>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
通过一个list生成一个新list

迭代器

先了解下什么是可迭代对象(Iterable)。我们知道,可以直接作用于 for 循环的数据类型有以下几种:

  • 一类是集合数据类型,如 list、 tuple、 dict、 set、 str 等;
  • 一类是 generator,包括生成器和带 yield 的 generator function。

这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable

  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
  • 迭代器有两个基本的方法:iter() 和 next()
  • 字符串,列表或元组对象都可用于创建迭代器
>>> list=[1,2,3,4]
>>> it = iter(list) # 创建迭代器对象
>>> print (next(it)) # 输出迭代器的下一个元素
1
>>> print (next(it))
2

可以使用 isinstance() 判断一个对象是否是 Iterable 对象: 

判断对象是否是 Iterable对象

可以被 next() 函数调用并不断返回下一个值的对象称为迭代器:Iterator。可以使用 isinstance()判断一个对象是否是 Iterator 对象: 把 list、 dict、 str等。

Iterable 变成 Iterator 可以使用 iter()函数:

为什么 list、 dict、 str 等数据类型不是 Iterator?

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

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

小结

  • 凡是可作用于 for 循环的对象都是 Iterable 类型;
  • 凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列;
  • 集合数据类型如 list、 dict、 str 等是 Iterable 但不是 Iterator,不过可以通过 iter()函数获得一个 Iterator 对象。

生成器(Generator)

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含 100 万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的 list,从而节省大量的空间。在 Python 中,这种一边循环一边计算的机制,称为生成器: generator
要创建一个 generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [] 改成 (),就创建了一个 generator:

 g = (x*x for x in np.arange(10))

创建列表生成式 L 和 迭代器 g 的区别仅在于最外层的 [](), L 是一个 list,而 g 是一个generator。除了用列表生成式的 for 循环方式创建 generator,还可以使用函数来实现,在 Python 中,使用了 yield 的函数被称为生成器(generator)。变成 generator 的函数,在每次调用 next()的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。如用 yield 实现斐波那契数列生成器函数:

斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到。

#!/usr/bin/python3
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成

while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

程序输出如下:

0 1 1 2 3 5 8 13 21 34 55

小结

generator 是非常强大的工具,在 Python 中,可以简单地把列表生成式改成 generator,也可以通过函数实现复杂逻辑的 generator。

参考资料

《廖雪峰-Python3教程》 
菜鸟教程-Python3

赞赏

发表评论

textsms
account_circle
email

  • harley博主

    写得不错,加油!

    1年前 回复

嵌入式视觉笔记

Python3 高级特性
本文为学习笔记,参考资料《廖雪峰-Python3教程》 目录 切片迭代列表生成式生成器迭代器 掌握了 Python 的数据类型、语句和函数,基本上就可以编写出很多有用的程序了…
扫描二维码继续阅读
2019-08-29