:序列数据容器)
目录一、什么是数据容器1. 为什么需要数据容器2. Python 常见数据容器二、列表1. 列表的声明与下标索引2. 列表的增删改查2.1 查2.1 增2.3 删2.4 改3. 常用内置函数4. 列表的循环遍历三、元组1. 元组的定义与读取2. 为什么元组不可修改3. 常用方法、内置函数与循环遍历4. 解包操作4.1 变量赋值解包4.2 函数调用中的解包传参四、字符串1. 字符串的特点2. 字符串常用方法2.1 文本切分与合并2.2 文本修剪与替换2.3 状态检索与前缀判定2.4 字母大小写转换3. 常用内置函数与遍历五、序列的通用操作1. 切片操作2. 共通操作符3. 共通内置函数4. List、Tuple、String 对比总结一、什么是数据容器在前几章中我们处理的数据是孤立的无论是数字还是单个布尔值它们一次只能表达一个状态。但在实际开发中我们需要管理成千上万的数据流——例如电商系统的商品列表、用户的敏感词文本等为了高效组织这些成组的数据Python 引入了数据容器。本篇博客将系统拆解 Python 中的容器序列并揭示它们背后相通的设计1. 为什么需要数据容器试想一下如果班级里有 30 个学生我们需要在程序中记录所有人的名字。在没有数据容器的情况下你可能需要声明 30 个独立的变量student1 张三 student2 李四 # ... 声明到 student30代码将变得极其难以维护这种做法不仅极其臃肿而且当班级人数动态增加时程序根本无法运行。数据容器是一种可以容纳多个元素的高效数据类型。它允许我们用一个变量名来管理成组的数据并提供了批量遍历、检索和统计的标准化接口2. Python 常见数据容器根据底层数据结构与组织方式的不同Python 的数据容器主要分为四大类列表List、元组Tuple、字符串String、字典Dictionary和集合Set而在这些容器中列表、元组、字符串拥有一个共同的底层身份序列Sequence在 Python 的世界里序列意味着在内存中都是连续排列的且全部原生支持下标索引、切片、拼接、成员运算等底层操作。这种统一的序列思维也是后续深入学习 NumPy 矩阵运算和 Pandas 数据分析的基石二、列表列表是 Python 中使用频率最高、功能最强大的数据容器。其具有以下四个特征有序性元素在内存中是连续有序排列的每个元素都有固定的物理下标支持双向索引可变性可以在不改变列表自身内存地址的前提下随时随地进行增、删、改元素无限制一个列表内可以同时塞入整型、浮点型、字符串、布尔值甚至是另一个列表允许重复列表内允许存在完全相同的元素1. 列表的声明与下标索引列表使用方括号进行定义内部元素之间使用英文逗号隔开# 声明一个可以容纳混合类型的列表 my_list [Python, 2026, True, 3.14]双向下标索引由于列表是序列它内部的元素是有序排列的。Python 为序列提供了天才般的双向索引机制正向索引从左往右从 0 开始递增反向索引从右往左从 -1 开始递减用于快速定位末尾元素names [张三, 李四, 王五] print(names[0]) # 输出张三第一个元素 print(names[-1]) # 输出王五倒数第一个元素2. 列表的增删改查列表是可变容器这意味着我们可以在不改变其内存地址的前提下动态修改其内部的元素2.1 查list.index(obj)查找某个元素在列表中第一次出现的下标若不存在则报错list.count(obj)统计某个元素在列表中出现的总次数tools [Python, Java, Python] print(tools.index(Python)) # 输出0 print(tools.count(Python)) # 输出22.1 增list.append(obj)在列表的末尾追加一个新元素高频list.insert(index, obj)在指定的下标位置插入元素后续元素自动后移list.extend(iterable)将另一个可迭代对象中的所有元素合并到当前列表中nums [1, 2] nums.append(3) # [1, 2, 3] print(nums) nums.insert(1, 99) # [1, 99, 2, 3] print(nums) nums.extend([4, 5]) # [1, 99, 2, 3, 4, 5] print(nums)2.3 删del list[index]根据下标直接删除指定位置的元素Python 内置关键字list.pop([index])移除并返回指定下标处的元素。如果不传参数默认移除并返回最后一个元素list.remove(obj)根据具体的值移除列表中第一次匹配到的元素若值不存在会报错list.clear()清空整个列表中的所有元素fruits [苹果, 香蕉, 橘子, 香蕉] del fruits[0] # 删除了苹果 popped_item fruits.pop() # 移除了最后的香蕉并返回 print(popped_item) fruits.remove(香蕉) # 移除了中间的香蕉 print(fruits) # 输出[橘子]2.4 改直接通过下标锁定位置并使用赋值运算符 赋予新值languages [C, C] languages[0] Python # 修改为 [Python, C] print(languages)3. 常用内置函数除了基础的增删改查Python 还提供了一系列强大的内置函数和列表自身的方法用于对数据进行统计、排序和重组。统计函数这三个全局函数不仅通用于列表也是未来处理海量数据的利器len(list)返回列表中元素的总个数max(list) / min(list)返回列表中的最大值或最小值要求内部元素类型一致且可比较如全为数值或全为字符串sum(list)计算列表中所有数值型元素的总和grades [78, 92, 85, 60, 100] print(len(grades)) # 输出5 print(max(grades)) # 输出100 print(sum(grades)) # 输出415原位排序与临时排序在对列表进行排序时Python 提供了两种完全不同的机制方法 Alist.sort()原位排序直接对当前列表内部的元素顺序进行调整会彻底改变原列表没有返回值函数 Bsorted(list)临时排序这是一个全局内置函数。它会复制一份原列表排好序后返回一个全新的列表对象原列表的顺序不发生改变# 案例 A原位排序 nums [3, 1, 4, 2] nums.sort() print(nums) # 原列表已被彻底修改输出[1, 2, 3, 4] # 案例 B临时排序 scores [30, 10, 40, 20] new_scores sorted(scores) print(scores) # 原列表不变输出[30, 10, 40, 20] print(new_scores) # 拿到了排好序的新列表输出[10, 20, 30, 40] # 拓展如果需要降序从大到小只需传入参数 reverseTrue # nums.sort(reverseTrue) 或 sorted(scores, reverseTrue)反转列表 list.reverse()不进行大小比较单纯将列表的物理顺序首尾彻底反转4. 列表的循环遍历遍历是指将列表中的每一个元素依次取出来执行相同的操作。在 Python 中遍历列表有两种最标准的方法直接迭代元素如果你只需要读取数据不需要知道或者修改元素的下标位置首选这种方式languages [Python, Java, Go] for lang in languages: print(f推荐学习语言{lang})利用 range() 结合下标控制如果你在遍历的过程中需要通过下标去修改原列表中的值应当使用这种方式prices [10, 20, 30] # 通过 len() 获取长度range() 生成 0, 1, 2 的下标序列 for i in range(len(prices)): # 将列表中的每件商品价格打八折 prices[i] prices[i] * 0.8 print(prices) # 输出[8.0, 16.0, 24.0]三、元组如果在开发中所有的容器都是可变的那就意味着程序中的关键数据随时面临着被后续代码误修改、误删除的风险。为了在解决这个问题Python 设计了元组其具有以下特征有序性与列表一致支持双向物理下标索引不可变性数据一旦写入便处于只读状态具备天然的线程安全性高效性由于结构固定元组在底层的内存开销比列表更小数据检索和加载的速度也更快1. 元组的定义与读取元组使用小括号进行定义内部元素之间使用英文逗号隔开。由于元组本质上也是序列它也继承了列表的下标索引机制# 定义一个存储服务器配置的元组 server_config (192.168.1.100, 8080, admin) # 使用物理下标读取元素正向、反向均可 print(server_config[0]) # 输出192.168.1.100 print(server_config[-1]) # 输出admin注意如果一个元组内部只包含一个元素在定义时必须在这个元素的后面加上一个英文逗号否则Python 解释器会将小括号误识别为数学运算符中的括号从而把它当成一个普通的字符串或数值fake_tuple (Python) # 没有逗号type(fake_tuple) 结果为 class str real_tuple (Python,) # 加上逗号type(real_tuple) 结果为 class tuple2. 为什么元组不可修改元组最核心的特性是不可变。一旦元组被创建任何尝试在其身上执行 append、insert、pop 或直接赋值修改的操作结果都会引发 TypeError 导致程序崩溃dimensions (200, 50) # dimensions[0] 250 # 报错相对不可变机制需要特别注意的是元组的不可变性指的是元组各个内存单元所指向的对象的引用内存地址不可改变如果元组内部嵌套了一个可变对象如列表那么这个列表内部的元素是可以被改变的因为这并没有改变元组本身指向该列表的物理地址# 元组包含一个不可变的数值和一个可变的列表 nested_tuple (10, [张三, 李四]) # 尝试改变列表内部的值 nested_tuple[1][0] 王五 print(nested_tuple) # 输出(10, [王五, 李四])3. 常用方法、内置函数与循环遍历由于元组是只读的它内部的方法比列表要精简得多仅保留了两个用于查找与统计的安全方法tuple.index(obj)查找元素第一次出现的下标位置tuple.count(obj)统计元素在元组中出现的总次数内置函数列表通用的内置统计函数元组同样可以调用nums_tuple (10, 20, 30, 20) print(len(nums_tuple)) # 获取长度输出4 print(nums_tuple.count(20)) # 统计计数输出2 print(sum(nums_tuple)) # 求和输出80元组的循环遍历因为不需要在遍历时修改元素元组通常直接采用最轻量、最具 Python 风格的遍历范式fruits (苹果, 香蕉, 橘子) for fruit in fruits: print(f水果{fruit})4. 解包操作解包是 Python 序列特有的语法糖。它允许我们一次性将元组或列表中的所有元素成批地赋值给多个独立的变量4.1 变量赋值解包一次性将元组中的所有元素成批地赋值给多个独立的变量# 声明一个包含 3 个元素的元组 user_info (Tom, 18, Beijing) # 执行解包变量的数量必须与元组中元素的数量严格一致 name, age, city user_info print(name) # 输出Tom print(age) # 输出18 print(city) # 输出Beijing快速交换两个变量的值在其他语言如 C, Java中交换两个变量需要引入一个第三方临时变量。但在 Python 中利用元组的隐式打包与解包一行代码即可优雅搞定a 10 b 20 a, b b, a # 右边隐式打包成元组 (20, 10)左边解包赋值 print(a, b) # 输出20 104.2 函数调用中的解包传参当一个函数需要接收多个位置参数而你手头的数据刚好被打包在一个元组中时你不需要通过 tuple[0]、tuple[1] 这种死板的方式挨个传参在调用函数时只需在元组变量前加上一个星号Python 就会自动把元组解包将其内部的元素按照先后顺序对应地传给给函数的形参# 定义一个计算长方体体积的函数需要 3 个确定的参数 def calc_volume(length, width, height): return length * width * height # 现成的数据被封装在一个配置元组中 box_dimensions (10, 5, 2) # 使用 * 号进行解包传参 # 它等价于calc_volume(10, 5, 2) volume calc_volume(*box_dimensions) print(f计算得出的体积为{volume}) # 输出100注意使用*解包传参时元组内元素的个数必须与函数要求的实参个数完全一致。如果元组里有 4 个元素而函数只要 3 个参数程序会直接崩溃四、字符串在前面的章节中我们已经频繁接触过字符串。现在我们需要给字符串贴上它真正的技术标签字符串是一个只读的字符序列。在 Python 中文本处理本质上都是对这一字符序列的操作。其具有以下特性有序性内部字符呈线性连续排列支持双向下标不可变性文本一旦生成任何修改行为都会导致新字符串的诞生类型单一性与列表、元组不同字符串容器的内部只能容纳单字符1. 字符串的特点既然字符串是序列它同样支持正反向物理下标且每个物理单元存储的就是单个字符text Python # 利用序列的下标进行读取 print(text[0]) # 输出P print(text[-1]) # 输出n不可变性字符串与元组一样其底层的物理结构是固定不可变的。你无法直接通过下标去修改、替换或删除字符串里的某个字符word python # word[0] P # 错误引发崩溃2. 字符串常用方法由于字符串在实际业务中承担了大量的文本清洗、日志解析和数据对齐工作Python 为其内置了极为丰富的处理方法所有字符串方法都不会改变原字符串它们在处理完毕后都会返回一个全新创建的字符串对象2.1 文本切分与合并str.split(sep)根据指定的分割符将字符串切片并拆分成一个列表str.join(iterable)以当前字符串作为连接符将传入的容器如列表中的所有字符串串联成一个新字符串# 数据清洗案例解析日志数据 raw_data 张三,18,北京 user_list raw_data.split(,) print(user_list) # 输出列表[张三, 18, 北京] # 将列表重新用波浪号组装 new_data ~.join(user_list) print(new_data) # 输出字符串张三~18~北京2.2 文本修剪与替换str.strip()移除字符串首尾两侧的所有空白字符包括空格、换行符 \n、制表符 \tstr.replace(old, new)将字符串中所有的 old 子串替换为 new 子串dirty_str \n admin_root \t clean_str dirty_str.strip() print(clean_str) # 输出admin_root sentence I love Java print(sentence.replace(Java, Python)) # 输出I love Python2.3 状态检索与前缀判定str.find(sub) / str.index(sub)查找子串第一次出现的下标。区别在于find 找不到时返回 -1而 index 找不到时会直接报错崩溃str.startswith(prefix) / str.endswith(suffix)判断字符串是否以特定的前缀或后缀开头/结尾返回布尔值filename report.xlsx # 快速判断文件类型 if filename.endswith(.xlsx): print(这是一个 Excel 表格文件)2.4 字母大小写转换str.lower()全部转换为小写常用于忽略大小写的用户登录验证str.upper()全部转换为大写3. 常用内置函数与遍历通用内置函数len(str)统计字符串中字符的总个数max(str) / min(str)根据 ASCII 编码的大小找出字符串中编码最大/最小的字符code ABC print(len(code)) # 输出3遍历字符串因为是序列直接使用 for...in 循环即可依次读取每一个字符message SOS for char in message: print(char, end-) # 输出S-O-S-五、序列的通用操作正如本章开头所强调的List、Tuple 和 String 在 Python 中并不仅仅是三个孤立的数据类型它们共同属于序列这意味着它们在底层的内存组织结构上有着高度的相似性。学会了列表的操作其核心逻辑可以平移到元组和字符串上1. 切片操作切片是指从一个现有的序列中截取出一部分连续的子序列。切片操作最优雅的地方在于它完全不破坏原有的序列实体而是复制并生成一个相同类型的全新子序列切片语法sequence[start:stop:step]start起始位置下标包含该位置。如果不写默认从最开头正向为 0反向为末尾开始截取stop结束位置下标不包含该位置。这遵循了数学中经典的左闭右开原则 [start, stop)。如果不写默认一路截取到序列末尾step步长控制截取时的间隔与推进方向。默认为 1。如果将其设为负数则代表从右向左进行逆序截取切片操作案例完全相同的切片语法在列表、元组和字符串中的运行规律完全一致# 1. 列表切片 nums_list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(nums_list[2:5]) # 截取下标 2 到 4输出[2, 3, 4] print(nums_list[:4]) # 缺省 start从头截取输出[0, 1, 2, 3] print(nums_list[1:8:2]) # 步长为 2 隔一个取一个输出[1, 3, 5, 7] print(nums_list[::-1]) # 逆序输出[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] # 2. 元组切片返回的依然是只读元组 nums_tuple (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) print(nums_tuple[2:5]) # 输出(2, 3, 4) print(nums_tuple[::-1]) # 输出(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) # 3. 字符串切片返回的依然是字符串 text_str 0123456789 print(text_str[2:5]) # 输出234 print(text_str[::-1]) # 输出98765432102. 共通操作符除了切片以下四个操作符在整个序列家族中具有完全统一的物理语义下标访问 [ ]支持正向 0 ~ N-1 以及反向 -1 ~ -N 的双向寻址序列拼接 将两个同类型的序列物理融合成一个全新的长序列序列重复 *将当前序列复制指定的次数生成一个冗余的新序列成员运算符 in / not in判断某个元素或子串是否存在于当前序列中返回布尔值True/False# 序列拼接 () print([1, 2] [3, 4]) # 列表拼接输出[1, 2, 3, 4] print(Hello World) # 字符串拼接输出Hello World # 序列重复 (*) print(- * 5) # 字符串重复输出----- print((0,) * 3) # 元组重复输出(0, 0, 0) # 成员检测 (in) print(5 in [1, 3, 5, 7]) # 列表检测输出True print(Py in Python) # 字符串子串检测输出True3. 共通内置函数Python 提供了四个全局内置函数专门用来提取任何序列的宏观统计状态len(seq)返回当前序列的成员总个数max(seq)返回当前序列中的最大值。min(seq)返回当前序列中的最小值。sum(seq)计算当前序列中所有元素总和注意这要求序列内部必须全为数值类型若是字符串序列调用 sum() 会直接报错my_list [10, 30, 20] my_tuple (10, 30, 20) my_str abc # c的 ASCII 码最大a的最小 print(len(my_list), max(my_tuple), min(my_str)) # 输出3 30 a4. List、Tuple、String 对比我们通过下表对这三个序列容器进行对比类型列表元组字符串声明方括号 [ ]小括号 ( )单/双/三引号 可变性可变只读只读数据限制可同时容纳任何类型可同时容纳任何类型只能容纳单字符性能较高需预留额外的扩容空间较低结构紧凑读取速度极快极低底层针对文本进行了深度优化总结本章围绕 Python 中的序列数据容器展开系统介绍了列表、元组和字符串的基本概念、特点及常用操作并进一步学习了序列之间共享的索引、切片、遍历、拼接等通用特性虽然三种数据容器都属于序列类型但它们在是否可修改、使用场景以及设计目的上各有侧重列表适合存储和维护可变数据元组适合保存不可变数据而字符串则专门用于文本处理。理解它们之间的联系与区别是后续学习 Python 数据结构的重要基础下一篇我们将继续学习另外两种常用的数据容器——集合和字典。它们虽然不属于序列类型却在去重、快速查找、键值映射等场景中发挥着不可替代的作用。届时我们还会对 Python 的五种核心数据容器进行系统对比并通过综合案例进一步掌握它们在实际开发中的应用