Python 中的序列详解

Python 中的数字类型(Number)、字符串类型(String)之后,后续应该要继续讲元祖(Tuple)、列表(List)之类的。但是元祖和列表都属于序列,掌握序列(Sequence)的相关知识可以帮我们更容易的学习后续内容,所以有必要先了解一下 Python 中的序列是什么?

Python 中的序列

序列是一系列数据元素的有序集合,注意是有序的,它是 Python 中最基本的一种数据结构。

序列中的每个元素都被分配一个 数字 —- 它的位置,也就是我们说的元素的 索引

第一个索引是 0,第二个索引是 1,依此类推……每个索引对应了一个 元素

Python 包含 6 种内建的序列,包括 字符串(str)元组(tuple)列表(list)、Unicode 字符串、buffer 对象以及 range(xrange) 对象。

对于序列,都可以使用以下操作:

  1. 索引
  2. 切片
  3. 加(+)
  4. 乘(*)
  5. 成员检查
  6. 计算序列的长度
  7. 取序列中的最大、最小值

is && == && id()

在开始正式的解读之前,我们先来看几个必要的语法:

>>> id()

Python 中的 id() 内置函数,可用于获取对象的内存地址。语法规则如下:

1
id(object)

样例如下:

1
2
3
4
5
6
>>> str1 = "id() function test"
>>> num = 1.23
>>> id(str1)
2419096572024
>>> id(num)
2419065860360

>>> is && ==

Python 中经常会用到对象之间的比较,可以用 ==,也可以用 is 。它们有什么区别?

  • is:比较的是 两个实例对象是不是完全相同是不是同一个对象(占用的内存地址是否相同)
  • ==:比较的是 两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了

样例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> str1 = "Python"
>>> str2 = str1

>>> str1 is str2
True

>>> id(str1)
2419095062488
>>> id(str2)
2419095062488

>>> str1 == str2
True

序列中的索引

就像上面说的,序列中的每个元素都分配一个 数字 —- 它的位置,也就是我们说的元素的 索引

[1] >>>> 两种索引方式:

正向索引:从前向后标记,第一个索引是 0,第二个索引是 1,依此类推。每个索引对应一个元素

反向索引:序列中元素的索引也可以从后向前标记,最后一个索引是 -1,倒数第二个索引是 -2,以此类推。

[2] >>>> 可以通过索引获取序列中元素:

通过索引和 [] ,我们可以获取到相应索引位置的元素对象:

1
2
3
4
5
6
7
8
9
10
>>> str_a = "Python"

>>> print(type(str_a))
<class 'str'>

>>> str_b = str_a[2]
>>> print(str_b)
t
>>> type(str_b)
<class 'str'>

可以发现,字符串序列中通过索引取到的元素类型仍为字符串。


有个上面的知识储备,这里再重新认识一下序列是什么?!!

序列到底是什么?

所谓序列,

指的是一块可存放多个值的连续内存空间,这些值按一定顺序排列,可以是任何类型的对象元素,可通过值所在位置的编号(称为索引)获取相应的元素对象,并且取到的是元素原对象。

1
2
3
4
5
6
7
8
9
10
11
>>> list1 = [1]

# 这里,你只需要了解 tup1 = () 可以创建一个元组即可
>>> tup1 = ("Python", 123, list1)

>>> test = tup1[2]
>>> print(test, type(test))
[1] <class 'list'>

>>> print(test is list1)
True

发现确实如此,也就是说,我们可以将序列中的索引看作是一个特殊的变量,它指向的是相应对象元素的内存空间,或着说,它对应了元素对象的引用。


字符串作为序列怎么解释?!! >>>>

不知道你发现没有:上面的说法套到字符串中略显变扭,因为字符串也是一个序列,难道字符串索引所对应的元素难道是一个个的字符(长度为一的字符串)对象???

我们先假设字符串是将每个字符(长度为一的字符串)都存储在一个个内存空间中(连续),字符串变量取得的是整个字符串,而索引则是每个字符的变量(引用)。来看一下假设结果如何:

1
2
3
>>> str_test = "abcdabcd"
>>> print(id(str1), id(str_test[0]), id(str_test[4]))
139820050579888 139820053906072 139820053906072

我们发现,哎,好像 感觉 假设成立啊?

那这样呢:

1
2
3
4
5
6
>>> str1 = "abcdabcd"
>>> str2 = 'a'
>>> str3 = 'b'
>>> str4 = 'b'
>>> print(id(str1[0]), id(str1[4]), id(str2), id(str3), id(str4))
2150485782008 2150485782008 2150485782008 2150485780328 2150485780328

看到这里你是不是明白了什么?!! >>>>

字符串是字符对象的引用的集合,所以我们使用英文的时候是不是最多只有创建 26 个字母,然后字符串只要重复引用就可以了。其他字符也同理。(是不有种常量缓存池的赶脚…)

关于常量缓存池请参见博文:Python 中的缓存重用机制(常量池)

有了上面的理解,是不对序列的了解就更深一步了?


序列支持操作

博文开篇已经说明了序列支持 7 中操作:

序列中的索引和切片

索引不用多介绍了,所谓切片就是通过 [:] 配合序列索引一次拿出多个对象,而索引一次只能拿出一个对象。

1
2
3
>>> str1 = "abcdabcd"
>>> print(str1[0:2])
ab

[1] >>>> 切片是前闭后开的:

上面返回的结果依然是一个字符串,这是要注意一个问题。还有就是 a[2] 明明是 c 才对,但结果我们并没有取到。

说明 >>>> 切片是不包括后界限的索引的,所以无法取到后界限索引所引用的元素(前闭后开)

切片的其它用法 >>>>

1
2
3
4
5
6
7
8
9
10
11
# 获取所有所有元素对象:
>>> print(str1[:])
abcdabcd
>>> print(str1[0:])
abcdabcd

# 注意,反向索引,无法取到后界限索引对象:
>>> print(str1[:-1])
abcdabc
>>> print(str1[-3:-1])
bc

[2] >>>> 切片小技巧:

先看一个元组切片的样例:

1
2
3
>>> tup1 = ("Python", 123, [1,2])
>>> print(tup1[:2], type(tup1[:2]))
('Python', 123) <class 'tuple'>

我们知道,对序列切片,只是从包含多个元素对象的序列中取出包含部分元素对象的序列。如上面元组切片,切片后仍然得到的是一个元组,可以得到几个元素对象。

此时,我们可以使用多个变量去接收这些元素,这样我们能得到独立的对象进行其它操作了:

1
2
3
4
5
6
7
8
9
>>> tup1 = ("Python", 123, [1,2])
>>> str1, num = tup1[:2]
>>> print(str1, num)
Python 123

>>> list1 = ["Google", "Edge", "Firfox"]
>>> test1,test2 = list1[0:2]
>>> print(test1, test2)
Google Edge

是不很方便?可以参见后面关于序列解包(Unpack)的说明。

[3] >>>> 切片中的步长概念:

所谓的切片步长,就是每几个元素对象取一个。如下:

1
2
>>> print(tup1[::2])
('Python', [1, 2])

可以看到,对于 tup1 = ("Python", 123, [1,2]) 我们每个 2 步长取一个元素的结果。

负向步长(即步长取值为负数) >>>> 可实现反序

1
2
3
4
5
>>> tup1 = ("Python", 123, [1,2])
>>> print(tup1[::2])
('Python', [1, 2])
>>> print(tup1[::-1])
([1, 2], 123, 'Python')

加(+)

1
2
3
4
5
6
7
>>> tup1 = ("Python", 123, [1,2])
>>> print(tup1 + tup1)
('Python', 123, [1, 2], 'Python', 123, [1, 2])

>>> str1 = "test"
>>> print(str1 + str1)
testtest

我们发现,同类型序列可以进行相加,并没有影响到原序列类型。

那么不同类型的序列相加呢? >>>> 不同类型序列不可直接相加!!!

1
2
3
4
>>> print(tup1 + str1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate tuple (not "str") to tuple

果然不行~~~


乘(*)

乘法和加法类似,只是几个一样的序列相加而已:

1
2
3
4
5
6
7
>>> tup1 = ("Python", 123, [1,2])
>>> str1 = "test"

>>> print(tup1 * 2)
('Python', 123, [1, 2], 'Python', 123, [1, 2])
>>> print(str1 * 2)
testtest

同样不会影响原序列。


成员检查(in && not in)

成员检查就是就是,判断元素对象是否(不)包含在序列中,返回的是布尔型值。如下:

1
2
3
4
5
6
7
8
9
10
11
12
>>> tup1 = ("Python", 123, [1,2])
>>> str1 = "test"

>>> print("te" in str1)
True
>>> print("te" not in str1)
False

>>> print(123 in tup1)
True
>>> print(123 not in tup1)
False

序列长度计算

其实就是 len() 内置函数的调用,该函数会返回序列容器中元素的数量。

1
2
3
4
5
>>> tup1 = ("Python", 123, [1,2])
>>> str1 = "test"

>>> print(len(tup1), len(str1))
3 4

后面,你会发现:不仅仅是序列,集合(Set)和字典(Dict)也可以通过 len() 求元素个数。


取序列中的最大、最小值

这里其实是内置函数 max()max() 的使用,其会返回序列中的最大、最小值。

关于不同序列最大、最小等到具体小节会介绍。


序列解包(Unpack)

序列中支持,通过序列解包的方式将序列中各元素对象分别赋给不同的变量,以获得单个元素的变量。

也就是说,可以直接使用序列元素个数对应数量的变量,来接收序列中的各个元素。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
str1 = "abc"
str2, str3, str4 = str1
print("str2: %s, str3: %s, str4: %s" % (str2, str3, str4))
# str2: a, str3: b, str4: c

list1 = ["Python", "C/C++", "Java"]
str5, str6, str7 = list1
print("str5: %s, str6: %s, str7: %s" % (str5, str6, str7))
# str5: Python, str6: C/C++, str7: Java

# 切片小技巧
list2 = ["Python", "C/C++", "Java"]
str8, str9 = list2[0:2]
print("str8: %s, str9: %s" % (str8, str9))
# str8: Python, str9: C/C++

新变量指向序列中各元素对象地址 >>>>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
list1 = ["Python", [1,2], {"good":2}]
str1, list2, dict1 = list1
print("str1: %s, list2: %s, dict1: %s" % (str1, list2, dict1))
# str1: Python, list2: [1, 2], dict1: {'good': 2}

print(id(str1), id(list1[0]))
print(id(list2), id(list1[1]))
print(id(dict1), id(list1[2]))
# 1609454214312 1609454214312
# 1609512943496 1609512943496
# 1609513158088 1609513158088

list2[0] = "update"
print(list1)
# ['Python', ['update', 2], {'good': 2}]

序列支持的内置函数

Python提供了几个内置函数,可用于实现与序列相关的一些常用操作:

函数 功能
len() 计算序列的长度,即返回序列中包含多少个元素。
max() 找出序列中的最大元素。
min() 找出序列中的最小元素。
list() 将序列转换为列表。
str() 将序列转换为字符串。
sum() 计算元素和。注意,对序列使用 sum() 函数时,做加和操作的必须都是数字,不能是字符或字符串,否则该函数将抛出异常,因为解释器无法判定是要做连接操作(+ 运算符可以连接两个序列),还是做加和操作。
sorted() 对元素进行排序。
reversed() 反向序列中的元素。返回一个逆序序列的迭代器(用于遍历该逆序序列)
enumerate() 将序列组合为一个索引序列,多用在 for 循环中。

Author

Waldeinsamkeit

Posted on

2018-01-06

Updated on

2022-04-05

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.