# 常用数据类型

在介绍 Python 的常用数据类型之前，我们先看看 Python 最基本的数据结构 - **序列（sequence）**。

**序列**的一个特点就是根据索引（index，即元素的位置）来获取序列中的元素，第一个索引是 0，第二个索引是 1，以此类推。

所有序列类型都可以进行某些通用的操作，比如：

- 索引（indexing）
- 分片（sliceing）
- 迭代（iteration）
- 加（adding）
- 乘（multiplying）

除了上面这些，我们还可以检查某个元素是否属于序列的成员，计算序列的长度等等。

说完序列，我们接下来看看 Python 中常用的数据类型，如下：

- [列表（list）](./list.md)
- [元组（tuple）](./tuple.md)
- [字符串（string）](./string.md)
- [字典（dict）](./dict.md)
- [集合（set）](./set.md)

其中，**列表、元组和字符串都属于序列类型，它们可以进行某些通用的操作**，比如索引、分片等；字典属于**映射**类型，每个元素由键（key）和值（value）构成；集合是一种特殊的类型，它所包含的元素是不重复的。

# 通用的序列操作

## 索引

序列中的元素可以通过索引获取，索引从 0 开始。看看下面的例子：

```
>>> nums = [1, 2, 3, 4, 5]   # 列表
>>> nums[0]
1
>>> nums[1]
2
>>> nums[-1]                 # 索引 -1 表示最后一个元素
5
>>> s = 'abcdef'             # 字符串
>>> s[0]
'a'
>>> s[1]
'b'
>>>
>>> a = (1, 2, 3)            # 元组
>>> a[0]
1
>>> a[1]
2
```

注意到，-1 则代表序列的最后一个元素，-2 代表倒数第二个元素，以此类推。

## 分片

**索引**用于获取序列中的单个元素，而**分片**则用于获取序列的部分元素。分片操作需要提供两个索引作为边界，中间用冒号相隔，比如：

```python
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> numbers[0:2]                   # 列表分片
[1, 2]
>>> numbers[2:5]
[3, 4, 5]
>>> s = 'hello, world'             # 字符串分片
>>> s[0:5]
'hello'
>>> a = (2, 4, 6, 8, 10)           # 元组分片
>>> a[2:4]
(6, 8)
```

**这里需要特别注意的是，分片有两个索引，第 1 个索引的元素是包含在内的，而第 2 个元素的索引则不包含在内**，也就是说，numbers[2:5] 获取的是 numbers[2], numbers[3], numbers[4]，没有包括 numbers[5]。

下面列举使用分片的一些技巧。

- 访问最后几个元素

假设需要访问序列的最后 3 个元素，我们当然可以像下面这样做：

```python
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> numbers[3:6]
[4, 5, 6]
```

有没有更简洁的方法呢？想到可以使用负数形式的索引，你可能会尝试这样做：

```python
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> numbers[-3:-1]      # 实际取出的是 numbers[-3], numbers[-2]
[4, 5]
>>> numbers[-3:0]       # 左边索引的元素比右边索引出现得晚，返回空序列
[]
```

上面的两种使用方式并不能正确获取序列的最后 3 个元素，Python 提供了一个捷径：

```python
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8]
>>> numbers[-3:]
[6, 7, 8]
>>> numbers[5:]
[6, 7, 8]
```

也就是说，如果希望分片包含最后一个元素，可将第 2 个索引置为空。

**如果要复制整个序列，可以将两个索引都置为空**：

```python
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8]
>>> nums = numbers[:]
>>> nums
[1, 2, 3, 4, 5, 6, 7, 8]
```

- 使用步长

使用分片的时候，步长默认是 1，即逐个访问，我们也可以自定义步长，比如：

```python
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8]
>>> numbers[0:4]
[1, 2, 3, 4]
>>> numbers[0:4:1]    # 步长为 1，不写也可以，默认为 1
[1, 2, 3, 4]
>>> numbers[0:4:2]    # 步长为 2，取出 numbers[0], numbers[2]
[1, 3]
>>> numbers[::3]      # 等价于 numbers[0:8:3]，取出索引为 0, 3, 6 的元素
[1, 4, 7]
```

另外，步长也可以是负数，表示从右到左取元素：

```python
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8]
>>> numbers[0:4:-1]
[]
>>> numbers[4:0:-1]       # 取出索引为 4, 3, 2, 1 的元素
[5, 4, 3, 2]
>>> numbers[4:0:-2]       # 取出索引为 4, 2 的元素
[5, 3]
>>> numbers[::-1]         # 从右到左取出所有元素
[8, 7, 6, 5, 4, 3, 2, 1]
>>> numbers[::-2]         # 取出索引为 7, 5, 3, 1 的元素
[8, 6, 4, 2]
>>> numbers[6::-2]        # 取出索引为 6, 4, 2, 0 的元素
[7, 5, 3, 1]
>>> numbers[:6:-2]        # 取出索引为 7 的元素
[8]
```

这里总结一下使用分片操作的一些方法，分片的使用形式是：

```python
# 左索引:右索引:步长
left_index:right_index:step
```

要牢牢记住的是：

- 左边索引的元素包括在结果之中，右边索引的元素不包括在结果之中；
- 当使用一个负数作为步长时，必须让左边索引大于右边索引；
- 对正数步长，从左向右取元素；对负数步长，从右向左取元素；

## 加

序列可以进行「加法」操作，如下：

```python
>>> [1, 2, 3] + [4, 5, 6]     # 「加法」效果其实就是连接在一起
[1, 2, 3, 4, 5, 6]
>>> (1, 2, 3) + (4, 5, 6)
(1, 2, 3, 4, 5, 6)
>>> 'hello, ' + 'world!'
'hello, world!'
>>> [1, 2, 3] + 'abc'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "str") to list
```

这里需要注意的是：两种相同类型的序列才能「加法」操作。

## 乘

序列可以进行「乘法」操作，比如：

```python
>>> 'abc' * 3
'abcabcabc'
>>> [0] * 3
[0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
```

## in

为了检查一个值是否在序列中，可以使用 `in` 运算符，比如：

```python
>>> 'he' in 'hello'
True
>>> 'hl' in 'hello'
False
>>> 10 in [6, 8, 10]
True
```

# 参考资料

- 《Python 基础教程》


