Wlgls 冲鸭!

第二章 Python数据结构


Python列表推导式和生成器表达式

列表推导式

语法: [exp for iter_var in iterable if condition]

其中if语句是可以省略的

在工作时我们迭代iterable中的元素并赋值于iter_var,最后通过exp来的到一个值添加到一个新的列表中。

简单的示例

>>> exp = [x for x in range(10)]
>>> exp
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

嵌套if语句

# 通过嵌套if语句来获得从0到9的所有偶数
>>> exp = [x for x in range(10) if x % 2 == 0]
>>> exp
[0, 2, 4, 6, 8]

多重for循环

如果我们需要遍历一个多维数组,正常情况下我们是这么做

>>> item = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> for i in range(3):
...     for j in range(3):
...             item[i][j]
... 
1
2
3
4
5
6
7
8
9

但是在列表推导式中,我们可以使用

item = [[1, 2, 3],
        [4, 5, 6], 
        [7, 8, 9]]

exp = [item[i][j] for i in range(3)
                  for j in range(3)]
print(exp)

[1, 2, 3, 4, 5, 6, 7, 8, 9]

当然列表推导式并不是仅仅这么使用,我们也可以使用它来进行笛卡尔乘积

list1 = [1, 2, 3]
list2 = [4, 5, 6]

exp = [(x, y) for x in list1
              for y in list2]

print(exp)

[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]

exp的多种形式

在前面中,只是简单的使用了x作为exp,但实际上是可以使用多种表达式的

>>> exp = [x ** 2 for x in range(10)]
>>> exp
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

我们甚至可以使用函数,自然lambda表达式也是可以使用的

>>> def f(x):
...     return x
... 
>>> exp = [f(x) for x in range(10)]
>>> exp
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

生成器表达式

在面对元组、数组或其他序列类型时,使用生成器表达式是一个很好的选择。因为生成器表达式背后遵循迭代器协议,可以逐个的产出元素。

生成器表达式的语法与列表推导式差不多,只不过把方括号改成了圆括号。

(exp for iter_var in iterable if condition)

举一个简单的例子

>>> exp = (x for x in range(10))
>>> exp
<generator object <genexpr> at 0x7f3c1ba0d7c8>

他返回的是一个生成器,对于这个生成器,他与iterator有很大的类似之处,他的遍历是不回头的,是一次性的

>>> exp
<generator object <genexpr> at 0x7f3c1ba0d7c8>
>>> for i in exp:
...     i
... 
0
1
2
3
4
5
6
7
8
9
>>> exp
<generator object <genexpr> at 0x7f3c1ba0d7c8>
>>> for i in exp:
...     i
... 
>>> 

Python元组不仅仅是不可变的列表

元组不仅仅是不可变的列表,我们可以使用元组来表示记录,元组中的每一个元素都存放了记录中一个字段的数据,而位置就赋予了这个数据意义

比如我们可以使用('Tokyo', 2003, 32450, 0.66, 8041),其中每一个元素都有自己的含义

>>> city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8041)
>>> city
'Tokyo'
>>> 

元组拆包

元组拆包其实是可以运行到任何一个可迭代对象上的,唯一的要求就是可迭代对象中的元素数量必须跟接受这个元素的元组的空档数一致。当然我们也可以使用*来改变这一情况

在上一个示例中就是一个元组拆包的情况,五个变量名与五个元素一一对应

>>> year
2003
>>> pop
32450
>>> chg
0.66

如果我们只想得到其中某些个值呢,我们可以使用_来做为占位符,但这不是一个很好的解决措施,我们更应该使用*来处理, *可以出现在任何位置,他会占用所有没有赋值的元素,并将其组成一个列表返回

# * 在后面
>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
# 在中间
>>> a, *body, b = range(5)
>>> a, body, b
(0, [1, 2, 3], 4)
#  在头部
>>> *head, a, b = range(5)
>>> head, a, b
([0, 1, 2], 3, 4)

具名元组

对于一条记录,我们规定了各个位置所代表的含义是什么,但在实际应用中,我们可以使用collections.namedtuple来进行更为简便的操作,需要注意的是,他并不会比元组消耗的内存大很多,所以使用它是完全可以的

>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates')
>>> xi = City('xi', 'dong', 123, (23, 45))
>>> xi
City(name='xi', country='dong', population=123, coordinates=(23, 45))
>>> xi.population
123
>>> xi.coordinates
(23, 45)
>>> 

collections.namedtuple

我们需要了解一下collections.namedtuple这个函数,你可以参阅Python官网

collections.namedtupletypenamefield_names*rename = Falsedefaults = Nonemodule = None 

# typename: 元组的名称
# field_names: 可以是字符串,由空格或着逗号也分割开字段名

# return: 一个命名为typename的新的元组子类

此外 namedtuple有一些十分重要的参数

  1. _fields

这会返回对应位置的参数值

>>> Point = namedtuple("Point", ['x', 'y'])
>>> p = Point(11, 22)
>>> p._fields
('x', 'y')
  1. _make(iterable)

如果我们有一个可迭代对象,我们可以使用_make,来生成新的实例

>>> q = (1, 2)
>>> Point._make(q)
Point(x=1, y=2)
  1. _asdict()

返回一个dict映射字段名称到他们对应的值

>>> p = Point(x=11, y=22)
>>> p._asdict()
OrderedDict([('x', 11), ('y', 22)])

Python切片

在初学python时,就已经提及python中切片的使用了,无论是列表,元组还是字符串,这类序列都支持切片操作

基本操作

对于切片操作,其基本格式是object[start_index:end_index:step]

  • start_index: 开始的位置,从0开始计数,默认为0
  • end_index: 结束的位置,默认为长度, 我们所切的元素,不包括结束位置的元素
  • step: 步长, 默认为1
>>> a = [1, 2, 3, 4, 5, 6]
>>> a[1:2]
[2]
>>> a[0:6]
[1, 2, 3, 4, 5, 6]
>>> a[0:6:2]
[1, 3, 5]

使用负数来切片

在python中,我们是可以使用负数来表示索引的,其中-1表示最后一个元素,所以在切片中适当的使用负数将会有极大的帮助

在切片中,step也是可以使用负数的,使用负数表示从最后一个元素开始计数

>>> a[0:-1]
[1, 2, 3, 4, 5]
>>> a[::-1]
[6, 5, 4, 3, 2, 1]
>>> a[::-2]
[6, 4, 2]

给切片赋值

对于切片操作,如果我们将切片放在赋值语句的左边,或者作为del操作的对象,那么,我们对序列的嫁接,切除是在原序列上修改的。但是这种修改必须保证在赋值语句的右边是可迭代对象。

>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[1:2] = [10]
>>> a
[0, 10, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[1:3]
[10, 2]
>>> a[1:3] = [12, 13]
>>> a
[0, 12, 13, 3, 4, 5, 6, 7, 8, 9]
>>> del a[0:1]
>>> 
>>> a
[12, 13, 3, 4, 5, 6, 7, 8, 9]
>>> a[4:5] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable