news 2026/9/26 10:23:03

Python有序序列完全指南:字符串/列表/元组/range的索引切片与常用方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python有序序列完全指南:字符串/列表/元组/range的索引切片与常用方法

我从后台翻了翻留言,发现很多刚接触Python的朋友都会卡在同一个地方:字符串、列表、元组、range,明明长得不一样,操作起来却总有相似之处。今天这篇就是来把这层窗户纸捅破的——有序序列(Sequence),其实就是Python里所有"能排队、能按序号取东西"的数据类型的总称。理解它,之后学切片、学遍历、学数据清洗,都会顺很多。

这篇不只讲概念,我会把索引、切片、通用操作、可变与不可变这几个核心知识点串成一条线,每个部分都有代码示例和踩坑提醒。适合刚学完变量和基本数据类型、准备进阶的初学者,也适合那些"Python语法会一点但总觉得自己写不熟"的老朋友。

1. 从一串字符串说起:到底什么才算"有序序列"

先做个最简单的实验。你写一句s = "hello",然后想取第二个字符,Python语法是s[1],出来的是e。你会发现字符串里的字母是按照固定顺序排列的,每个字都有自己的位置编号,这个"位置编号"就是索引。再写一个列表lst = ["苹果", "香蕉", "橘子"],想取第二个水果,也是lst[1]。两个完全不同类型的东西,操作方式一模一样,这背后就是同一个逻辑:它们都是有序序列。

1.1 有序序列的四个主要成员

Python里有四大常见有序序列,你最好把它们的名字和特征刻在脑子里:

  • 字符串(str):不可变的字符序列。"Python"就是一串排好队的字符,里面每个字母都占一个位置。
  • 列表(list):可变的容器序列。能往里加东西、删东西、改东西,日常开发最常用。
  • 元组(tuple):不可变的容器序列。一旦创建,里面的元素就不能增删改,适合存放"不该被修改"的数据。
  • range对象:表示一个整数范围的序列。它本身不把所有数字一次性存下来,而是"惰性"生成,用来配合循环非常舒服。

这四个成员虽然具体用途不同,但都支持一整套"通用序列操作":索引、切片、拼接、成员判断、长度计算、遍历等等。这也是为什么标题里强调"基本方法"——这些方法不是在某个类型里单独存在的,而是有序序列这个大家族共享的通用规则。

1.2 为什么说"有序"这两个字是破局点

很多初学者想不通:set(集合)和dict(字典)也能存多个数据,为什么它们不算有序序列?

关键就在"有序"二字。有序意味着每一个元素都有确定的位置编号,你可以通过编号直接锁定它、可以按照顺序一片一片地取出来、可以判断谁在前谁在后。而set和dict是基于哈希表实现的,存储顺序由哈希值决定,所以它们不支持索引,也不支持切片。换句话说,"有序"是一切高级操作的基石。

1.3 前期怎么理解"方法"和"函数"这两个词

标题里的"方法"指的是依附在对象上的函数。字符串、列表、元组本身自带一些功能函数,比如lst.append(4)就是调用列表的append方法,s.upper()就是调用字符串的upper方法。而len()、max()这类是内置函数,直接传参数进去用。最开始不需要把这两者分得太死,你只需要知道:有序序列自带一批工具,内置函数又给了一批通用工具,两批配合着用就行。

2. 索引与切片:序列操作的第一把钥匙

索引和切片是有序序列最核心的两个操作。索引负责"单个精准打击",切片负责"范围成片收割",两者配合起来几乎可以应付所有取值需求。

2.1 正索引:从0开始的游戏规则

Python的索引起点是0,不是1。"hello"[0]得到的是h,"hello"[1]得到的是e。这个规则对列表、元组、range全都适用。新手最容易在这翻车:想取第一个元素却写lst[1],拿到的其实是第二个。

记忆技巧:把索引理解为"偏移量"。第一个元素距离起点偏移0次,所以是[0];第二个元素偏移1次,所以是[1]。

lst = [10, 20, 30] print(lst[0]) # 10 print(lst[1]) # 20 print(lst[2]) # 30

2.2 负索引:从尾巴往前数的野路子

Python有个独门绝技——负索引。lst[-1]拿到的是最后一个元素,lst[-2]拿到的是倒数第二个。很多新手一开始觉得很怪,但用习惯之后会真香,尤其是在不知道序列长度的时候,想取最后一个元素根本不需要先len()再算下标。

name = "Python" print(name[-1]) # n print(name[-2]) # o

注意:索引越界会直接报IndexError。比如lst = [10, 20, 30],你写lst[5]程序立刻崩溃。但切片不会,这点放到下一节说。

2.3 切片:一次取出一片,左闭右开是灵魂

切片的基本语法是序列[start:stop:step],表示从start开始,到stop之前结束,每次按step步长取一个。这里最反直觉的就是"到 stop 之前结束",也就是左闭右开规则。

nums = [0, 1, 2, 3, 4, 5] print(nums[1:4]) # [1, 2, 3],注意没有4 print(nums[:3]) # [0, 1, 2],start省略表示从头开始 print(nums[2:]) # [2, 3, 4, 5],stop省略表示一直到结尾 print(nums[:]) # 整个序列的副本

为什么设计成左闭右开?因为这样能让切片长度直接等于stop - start。比如nums[1:4]长度就是 4-1=3,不用再纠结加不加一。而且这种设计在代码里到处都在用,比如range(5)生成的是0,1,2,3,4,天然就不包含5,习惯之后会觉得很统一。

2.4 步长与反转:切片的高级玩法

步长默认是1,你可以改成2、3等,实现跳跃取样:

nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(nums[::2]) # [0, 2, 4, 6, 8],隔一个取一个 print(nums[1::2]) # [1, 3, 5, 7, 9]

步长也可以是负数,负步长会让切片从右往左走,最经典的用法就是[::-1]实现序列反转:

s = "Python" print(s[::-1]) # nohtyP

不过这里有个大坑:如果start和stop没配合好,负步长容易得到空结果。比如"Python"[0:5:-1],逻辑上你想从前往后取,但步长是负数让它想从后往前,矛盾了,结果就是空串。正确写法应该是"Python"[5::-1]。

切片越界不报错,这点和索引完全不同。nums[1:100]不会报错,只会把从第1个到结尾的所有元素返回。这个宽容特性在数据清洗时很有用,不用担心边界溢出。

2.5 切片的复制属性:别把引用和副本搞混

切片返回的是一个新的序列对象。对列表来说,lst[:]得到的是一份浅拷贝,修改新列表不会影响原列表。这个特性经常被用来快速复制列表:

a = [1, 2, 3] b = a[:] b.append(4) print(a) # [1, 2, 3] print(b) # [1, 2, 3, 4]

但注意,如果列表里装的是可变对象(比如子列表),切片浅拷贝只复制外层,内层的子列表仍然是同一个对象。这个深浅拷贝问题后面单独说,现在心里有根弦就行。

3. 通用操作全家桶:每个序列都能用的能力

除了索引和切片,有序序列还共享一批"通用操作"。这些操作不属于某个具体类型,而是所有有序序列都支持的规则,学一次,四处生效。

3.1 拼接与重复:加号与乘号的真面目

+可以用来拼接两个相同类型的序列,字符串拼接成更长字符串,列表拼接成新列表。*可以重复序列若干遍,生成拼接后的新序列。

s1 = "Py" s2 = "thon" print(s1 + s2) # Python lst1 = [1, 2] lst2 = [3, 4] print(lst1 + lst2) # [1, 2, 3, 4] print(lst1 * 3) # [1, 2, 1, 2, 1, 2] print("ab" * 3) # ababab

关键点:拼接和重复都不会修改原序列,而是生成新序列。字符串和元组本来就是不可变的,列表哪怕可变,用+也是返回新列表。如果你想在原列表基础上追加,应该用extend()方法或在尾部拼接,而不是+之后重新赋值。

3.2 成员判断:in 和 not in

in用来判断某个元素是不是在序列中,结果是布尔值。这个操作简单但极其常用,比如检查用户名是否在列表中、判断某个字符是否出现在字符串中。

fruits = ["苹果", "香蕉", "橘子"] print("苹果" in fruits) # True print("西瓜" in fruits) # False s = "hello python" print("py" in s) # True,注意字符串的in是子串匹配

字符串的in稍微特殊一点:它判断的是子串,不是单个字符。如果有半句话,切片的返回值是新序列,而index返回的是整数值(位置下标),两者完全不冲突,只是它们分别服务于"取数据"和"找位置"两个需求。

3.3 len、max、min、sum:内置函数大礼包

  • len()返回序列长度,也就是里面有多少个元素。
  • max()返回序列中最大的元素。
  • min()返回序列中最小的元素。
  • sum()返回序列中所有元素之和,注意它只对数值管用。
nums = [5, 2, 8, 1, 9] print(len(nums)) # 5 print(max(nums)) # 9 print(min(nums)) # 1 print(sum(nums)) # 25

这几个函数能直接用于任何有序序列。加一个细节:max和min对字符串序列按字典序比较,对数值序列按数字大小比较;如果你对字典列表求max,直接做会报错,必须先指定比较关键字的参数,这个属于进阶内容,入门阶段可以暂时不碰。但即使现在用不上,也要记住**"能支持就支持,不支持的等遇到再解决"**,不用一头扎进所有"特殊场景"。

3.4 序列比较:字典序规则

有序序列可以比较大小,规则是"从左到右逐元素比较",直到出现第一个不同的元素,谁大谁决定结果。这跟字典排序逻辑一致,所以叫字典序。

print([1, 2, 3] < [1, 2, 4]) # True,列表逐元素比较 print("abc" < "abd") # True,字符串逐字符比较 print("Python" < "python") # True,大写字母在小写字母前面

我见过不少初学者在排序后不理解为什么"banana"会排在"Banana"前面,其实就是因为字典序先比较第一个字符,'B'的ASCII码(66)小于'b'(98),所以大写字母总是先出现。

3.5 查找位置:index方法的基本用法

前面提到index方法,这里正式说一遍。基本语法是序列.index(要查找的元素),返回第一个匹配元素的下标。如果元素不存在,会抛出ValueError。

words = ["a", "b", "c", "b"] print(words.index("b")) # 1,返回第一个b的位置

如果你只想检查"有没有",先in判断再做index是更稳的写法:

if "b" in words: pos = words.index("b") print("找到了,位置在", pos) else: print("没有这个元素")

4. 可变与不可变:列表和元组的分界线

有序序列内部又分成两派:可变序列和不可变序列。列表是可变派的代表,字符串和元组是保守派的代表。这一章内容会在你写实用代码时报很多坑,值得认真看。

4.1 可变序列:列表的增删改查

列表支持在原对象上直接修改。常用方法有:

lst = [1, 2, 3] lst.append(4) # 在结尾追加一个元素 -> [1, 2, 3, 4] lst.insert(0, 0) # 在指定位置插入一个元素 -> [0, 1, 2, 3, 4] lst.extend([5, 6]) # 在结尾拼接一个可迭代对象 -> [0, 1, 2, 3, 4, 5, 6] lst.pop() # 弹出并返回末尾元素,也可以pop(下标) lst.remove(2) # 删除第一个匹配的元素 lst[0] = 100 # 通过索引直接修改 -> [100, 1, 2, 3, 4, 5, 6]

重要提醒:append和extend的区别是头号混淆点。append是把参数当作一个元素追加进去;extend是把参数里的每个元素逐个拼进去。看例子:

a = [1, 2] a.append([3, 4]) print(a) # [1, 2, [3, 4]],列表变成了3个元素 b = [1, 2] b.extend([3, 4]) print(b) # [1, 2, 3, 4],列表还是4个整数

这种错误我见过太多次了,尤其是想把一个列表合并到另一个列表的时候,用错append会得到嵌套列表,遍历时逻辑全乱。

4.2 不可变序列:字符串和元组的"改不动"

字符串和元组不能通过索引修改,也不能直接添加元素。你写s = "abc"; s[1] = "B",立刻报TypeError: 'str' object does not support item assignment。很多新手会疑惑:"那我想改字符串怎么办?" 答案是:创建新字符串,而不是修改旧的。

s = "hello" s2 = s.replace("h", "H") # 返回新字符串,原s不变 print(s) # hello print(s2) # Hello t = (1, 2, 3) t2 = t + (4,) print(t2) # (1, 2, 3, 4)

元组也一样,想"加元素"是重新拼接创建新元组,原元组纹丝不动。这里请注意一个小语法细节:写单元素元组必须带逗号,(4,)是元组,(4)只是整数4加了个括号。

4.3 为什么需要不可变序列

很多初学者会问:"既然列表这么灵活,为什么要用元组?" 核心原因有两个。

第一个是数据保护。有些数据在逻辑上不该被修改,比如颜色配置里的RGB值、数据库里的一行记录,如果写成元组,就杜绝了后面代码不小心改掉它的可能。第二个是可哈希性。元组可以当作字典的键,列表不行,因为字典要求键必须是不可变的。这个点等你学到字典时会很有感受,先混个眼熟。

字符串不可变带来的好处是:任何字符串操作都不会产生副作用,用它做数据处理的逻辑更容易推演和控制。

4.4 可变序列的引用陷阱:变量不是盒子

这个概念我称之为"引用陷阱",新手几乎必踩。以下写法的逻辑是什么?

a = [1, 2, 3] b = a b.append(4) print(a) # ??

很多新手以为a还是[1, 2, 3],但实际上a也会变成[1, 2, 3, 4]。原因是b = a并没有复制列表,而是让b和a指向了同一个列表对象。这就像两个人共用同一台冰箱,你往里放东西,另一个人打开时也会看到。

如果真想复制一份独立列表,用a[:]或list(a):

a = [1, 2, 3] b = a[:] # 基于切片复制一个新列表 b.append(4) print(a) # [1, 2, 3] print(b) # [1, 2, 3, 4]

字符串和元组因为不可变,天然没有这个烦恼——你修改变量只是换了个新对象,原来的对象不会被影响。

5. 常用方法盘点:count、index、sort、reverse

现在按功能类别把有序序列的常用方法过一遍。这节会花不少篇幅,因为很多方法看起来差不多,选错的概率很高。

5.1 count与index:统计与定位

count(x)统计x在序列中出现的次数;index(x)返回x第一次出现的位置。

nums = [1, 2, 3, 2, 2, 5] print(nums.count(2)) # 3 print(nums.index(2)) # 1

字符串同样支持,且更强一些,还能指定起始范围:

s = "hello world" print(s.count("o")) # 2 print(s.index("world")) # 6

容易踩坑的点:index在元素不存在时会抛异常,如果要写健壮代码,要么先in判断,要么用try/except。入门阶段建议先in判断,逻辑更直观。count不会报错,不存在就返回0。

5.2 sort与sorted:排序到底用哪个

这是Python里最常用的排序方式,两者经常被混淆。

  • list.sort()是就地排序,直接修改原列表,返回None。
  • sorted(seq)是返回新列表,原序列不变。
nums = [3, 1, 4, 1, 5] nums.sort() print(nums) # [1, 1, 3, 4, 5],原列表被改了 nums2 = [3, 1, 4, 1, 5] sorted_nums = sorted(nums2) print(nums2) # [3, 1, 4, 1, 5],原列表没变 print(sorted_nums) # [1, 1, 3, 4, 5]

一个经典错误是:

nums = [3, 1, 4] new_nums = nums.sort() print(new_nums) # None!因为sort()不返回新列表

如果你想要"原列表不变,同时得到新排序列表",用sorted;如果你只想对列表原地排个序,用sort。还有人会问:字符串和元组能sort()吗?不能,只有列表有sort()方法。但字符串和元组都可以用sorted(),结果返回列表。

5.3 reverse与reversed:反转也有两支人马

list.reverse()是就地反转,返回None。reversed(seq)是内置函数,返回一个反向迭代器,需要用list()或循环取出来。

nums = [1, 2, 3] nums.reverse() print(nums) # [3, 2, 1] nums2 = [1, 2, 3] rev_nums = list(reversed(nums2)) print(rev_nums) # [3, 2, 1] print(nums2) # [1, 2, 3],原序列不变

这种"就地方法返回None,内置函数返回新结果"的成对设计,在Python里经常出现,记住这个规律可以少走很多弯路。

5.4 字符串的独门方法:split、join、replace、strip

虽然这章的标题是"序列的基本方法",但字符串作为最常用的有序序列,有几个特殊方法必须掌握,因为它们用得太频繁了。

s = "apple,banana,orange" parts = s.split(",") # ['apple', 'banana', 'orange'] new_s = "-".join(parts) # apple-banana-orange s2 = " hello " print(s2.strip()) # "hello",去掉两端空白 s3 = "I love Python" print(s3.replace("love", "like")) # I like Python

split按分隔符把字符串切成列表;join是反操作,把列表里的字符串用指定分隔符合并成新字符串。这两个几乎永远成对出现,做数据处理时经常会用到。注意join的语法有点反直觉:分隔符在前,被合并的列表在后,即"分隔符".join(列表)。写反了会直接报错,多练几次就好。

5.5 元组的方法有多"贫瘠"?

元组因为不可变,只有count和index两个方法,没有增删改、没有排序和反转。这个"贫瘠"恰恰是它的设计特色——能做的操作越少,越不容易被误用。需要更丰富功能时,把元组转成列表再操作即可。

6. 遍历与实战:把方法用起来才算学会

学了一大堆方法,如果只停留在读懂代码的层面,很快会忘。真实场景里,有序序列最常和循环绑在一起使用。下面通过几个小程序把前面所有知识串起来。

6.1 基础遍历:for循环直接读序列

fruits = ["苹果", "香蕉", "橘子"] for fruit in fruits: print(fruit)

这段代码会依次打印苹果、香蕉、橘子。字符串、元组、range都可以这样遍历,这是最自然的遍历方式。

6.2 带上索引的遍历:enumerate函数

如果你需要同时知道元素内容和元素序号,用enumerate:

fruits = ["苹果", "香蕉", "橘子"] for idx, fruit in enumerate(fruits): print(idx, fruit) # 0 苹果 # 1 香蕉 # 2 橘子

enumerate返回的是"索引+元素"的配对,解包成两个变量来接收,代码简洁还不容易丢掉序号。如果你想起始序号从1开始,可以写成enumerate(fruits, start=1)。

6.3 实战一:用列表和切片做一个简易排行榜

假设你有一个成绩列表,需求是:"取出前三名,并保留原始顺序"。

scores = [78, 92, 85, 96, 88] sorted_scores = sorted(scores, reverse=True) # 从高到低排序 top3 = sorted_scores[:3] print(top3) # [96, 92, 88] # 如果还想知道前三名在原列表中的顺序呢? # 先把索引和分数打包成元组列表 pairs = list(enumerate(scores)) pairs.sort(key=lambda x: x[1], reverse=True) top3_pairs = pairs[:3] print(top3_pairs) # [(3, 96), (1, 92), (4, 88)]

这里使用了匿名函数lambda x: x[1]告诉sort按元组的第二个元素排序,也就是分数。这种做法在处理"带附件的排序"时非常实用,你会频繁用到 "key=" 参数。

6.4 实战二:字符串清洗与统计

读取一行句子,统计每个单词出现的次数。这个需求在文本分析里很常见。

text = "python is great and python is easy" words = text.split() # 默认按空白字符分割 word_count = {} for word in words: word_count[word] = word_count.get(word, 0) + 1 print(word_count) # {'python': 2, 'is': 2, 'great': 1, 'and': 1, 'easy': 1}

我先用split()把句子切分成单词列表,然后遍历列表,用字典做次数累加。get(word, 0)表示"如果字典里没有这个单词,就返回0",这样第一次遇到单词时能顺利从0加起。

6.5 实战三:用range构造等差数列

range是有序序列,经常配合for循环造出有规律的数列。

# 打印1到10之间所有偶数 for num in range(2, 11, 2): print(num) # 2 4 6 8 10 # 转成列表也方便 nums = list(range(5)) print(nums) # [0, 1, 2, 3, 4]

range(2, 11, 2)从2开始,到11之前结束,步长为2,正好生成偶数序列。理解左闭右开后,这种边界估算会特别快:你只需要知道"到11之前结束"就永远不会包含11。

6.4 练习建议:别只看,动手敲

这篇内容对初学者来说算密度很高了,最好的消化方式是把每个示例代码都不是复制,而是自己敲一遍,然后做这几件事:

  • 把示例里的列表换成元组,试试哪些方法会报错、哪些能用;
  • 把字符串"Python"做一遍[::-1],手动算出结果再打印验证;
  • 写一个程序,输入一句英文,输出每个单词的长度列表;
  • 创建列表[5, 3, 8, 1, 9, 2],分别用sort()和sorted(reverse=True)看差异。

我自己当年学这章时,最大的收获不是记住了每个方法,而是理解了"方法会返回None还是新序列"这个分水岭。你只要看到就地修改的方法(append、extend、sort、reverse)都返回None,看到生成新对象的方法(sorted、reversed、切片)都保留原序列不变,80%的混淆都能消除。

有序序列是Python数据操作的地基,地基稳了,后面学字典、集合、函数式编程都会轻松很多。下一篇我会接着聊字典和集合这类映射型数据结构,看看它们如何和今天学的序列配合使用。先动手把今天的代码跑起来,有问题评论区见。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 10:21:55

Open-Meteo 本地部署:3 步快速自建免费气象数据 API

Open-Meteo 本地部署&#xff1a;3 步快速自建免费气象数据 API 【免费下载链接】open-meteo Free Weather Forecast API for non-commercial use 项目地址: https://gitcode.com/GitHub_Trending/op/open-meteo Open-Meteo 是一个开源的气象数据平台&#xff0c;把 NOA…

作者头像 李华
网站建设 2026/9/26 10:20:52

在 Visual Studio 2026 中配 TaoToken:减少升级等待,把时间还给编码

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 10:20:50

服务端带宽压测与瓶颈排查:万人同服状态下的吞吐瓶颈

服务端带宽压测与瓶颈排查&#xff1a;万人同服状态下的吞吐瓶颈在大型多人在线游戏&#xff08;MMO&#xff09;或超大规模同屏竞技场景中&#xff0c;网络子系统的吞吐能力直接决定了服务器的承载上限。许多项目在内网测试时 500 人运转良好&#xff0c;但压测一旦推升至 5,0…

作者头像 李华
网站建设 2026/9/26 10:20:43

PyTorch Tensor布局与拷贝:从内存地址到GPU性能实战

1. 项目概述&#xff1a;为什么Tensor的布局与拷贝不是“复制粘贴”那么简单&#xff1f;你写完x torch.tensor([1, 2, 3])&#xff0c;再敲一句y x.clone()&#xff0c;心里想的是“好了&#xff0c;两个独立的张量”&#xff0c;结果模型训练突然报错RuntimeError: one of …

作者头像 李华