跳至主要内容

Python 切片:日常编码中的实用方法

了解切片如何轻松地提取、重排和分析您的数据。运用负索引、多维切片以及高级步长,获得精确掌控。
· 8分钟 阅读

使用 AI 探索

如果您已经厌倦了在令人困惑的切片索引间手忙脚乱,或总在疑惑为什么切出来的列表不如预期,那您来对地方了。在本指南中,我们将把 Python 切片拆解成一口一口容易消化的知识点(故意的双关)。从切片语法基础到多维数组的高级技巧都会覆盖。此外,我还会分享一些可以立即上手的实用示例。

如果您希望夯实 Python 基础,也可以查看我们的 Python Fundamentals 课程,专为掌握切片、数据结构等核心技能而设计。

理解 Python 切片

先快速了解一下在 Python 中切片的含义,以及为什么它有用。

什么是 Python 中的切片?

在 Python 中,切片是一种从序列(如字符串、列表、元组和 range)中提取特定片段的方法。切片既可以使用内置的 slice() 函数,也可以用更常见的方括号表示法:[start:end:step]。该能力是 Python 语言的核心组成部分,所以您无需导入任何其他包就能进行切片。在外部库(如 NumPy 或 pandas)中的切片有一些有趣的差异,我会在下文详细介绍。

为什么要在 Python 中使用切片?

Python 切片是进行数据操作最高效、最直观的工具之一。无论是分析数据集、处理字符串,还是操作数组,切片都能帮助您访问并处理数据的子集。

每位开发者都该掌握它的原因如下:

  1. 代码简洁易读:切片省去了重复循环或复杂条件的需求,让代码更短、更干净、更易调试。
  2. 高效的数据处理:不同于手动索引,切片在性能上做了优化。Python 的内部机制保证了切片操作的速度和内存效率。
  3. 跨数据结构的通用性:从字符串、列表到更高级的结构如 NumPy 数组和 pandas 数据框,切片都能无缝工作。
  4. 真实场景的应用:切片能简化日常编码任务。需要只处理数据集的一部分?从表格中取出特定列?或者为文本分析反转字符串?切片就是您的首选。

Python 中的基础切片方法

如前所述,切片是 Python 的核心功能,允许开发者从列表、字符串、元组等序列中提取片段。无需导入任何模块,Python 提供两种主要方式进行切片:: 语法和 slice() 函数。理解这两种方法很有用,因为您很可能都会遇到。

使用冒号语法进行 Python 切片

最常见的访问序列部分的方法是切片语法 sequence[start:stop:step]。三个参数——start、stop 和 step——分别控制切片的行为:

  • start:切片开始的索引(包含)。若省略,默认为 0。

  • stop:切片结束的索引(不包含)。若省略,默认为序列长度。

  • step:元素之间的间隔。若省略,默认为 1。

我们来看一个示例:

numbers = [10, 20, 30, 40, 50, 60]  

print(numbers[1:4])  
# Output: [20, 30, 40]  

print(numbers[:3])   
# Output: [10, 20, 30]  

print(numbers[::2])  
# Output: [10, 30, 50]  

您也可以将这种切片方法用于字符串:

text = "Python Slicing"  

print(text[7:])   
# Output: "Slicing"  

print(text[::-1]) 
# Output: "gnicilS nohtyP" (reverses the string)  

您甚至可以用切片从列表中提取交替元素:

data = [100, 200, 300, 400, 500]  

alternate = data[::2]  

print(alternate)  
# Output: [100, 300, 500]  

在真实场景中,处理大型数据集时切片尤其有用。比如您在处理一个包含数百万条记录的日志文件,需要提取最后 1,000 条记录进行分析:

logs = load_large_dataset()  
recent_logs = logs[-1000:]  

这种方式避免将整个数据集加载到内存中,确保应用高效运行。

使用 Python 的 slice() 函数

Python 的 slice() 函数提供了另一种方式:将切片参数定义为可复用的切片对象。这些对象封装了切片逻辑,可应用于多个序列。

语法与用法

slice() 函数的格式如下:

slice(start, stop, step)  

示例如下:

# Create a slice object
slice_obj = slice(1, 4)

# Apply to a list
numbers = [10, 20, 30, 40, 50]
print(numbers[slice_obj])  # Output: [20, 30, 40]

# Apply to a string
text = "Python"
print(text[slice_obj])  # Output: "yth"  

Python slice() 的优势

就我个人而言,我喜欢使用 slice() 函数,因为它允许我在不同序列间复用同一个切片对象,避免重复的切片逻辑。这也让代码更易读、更易维护。

如下面示例所示,我们只需定义一次切片对象,便可在多个序列中复用。这样无需反复指定相同的 start、end 和 step 值。它还提高了可复用性,因为只需在一个地方修改切片边界,所有使用该切片的地方都会自动更新。

# Define a reusable slice
my_slice = slice(2, 5)

# Apply to multiple sequences
data_list = [100, 200, 300, 400, 500]
data_string = "SlicingExample"

print(data_list[my_slice])  # Output: [300, 400, 500]
print(data_string[my_slice])  # Output: "ici"  

两种方法的比较

两种切片方式在 Python 中各有用武之地。冒号语法适合简洁操作,而 slice() 则在可复用和动态的切片逻辑中非常有价值。

特性语法 slice()函数
易用性适合快速切片任务更利于复用逻辑
可读性适用于直观的切片复杂切片更清晰
通用性代码中多为一次性使用可跨多个序列复用

库中的 Python 切片

切片不仅限于 Python 内置数据结构,它还延伸到诸如 NumPy 和 pandas 等强大库,这些库在数据处理方面不可或缺。尽管基本原则一致,这些库根据其数据结构引入了更多切片能力。

NumPy 中的切片

NumPy 数组将切片提升到新的层次,提供了处理大型多维数据集的强力工具。NumPy 切片的一个关键差异在于视图与拷贝:对 NumPy 数组进行切片通常返回视图(即对原始数据的引用),而不是新拷贝。这对处理大型数据集非常高效,但也需要小心,避免无意修改。

一维数组切片

import numpy as np  

# Create a 1D array
array = np.array([10, 20, 30, 40, 50])

# Slice elements from index 1 to 3
print(array[1:4])  
# Output: [20 30 40]  

# Apply step
print(array[::2])  
# Output: [10 30 50]  

多维数组切片

对于多维数组,切片会独立作用于每个轴。

# Create a 2D array
array_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# Slice the first two rows and first two columns
print(array_2d[:2, :2])  
# Output:
# [[1 2]
#  [4 5]]  

# Slice the last column
print(array_2d[:, -1])  
# Output: [3 6 9]  

NumPy 切片的优势

  1. 内存高效:对切片进行操作避免数据拷贝,加速计算。
  2. 灵活性:多维切片让您能精确控制数据子集。

想要更深入了解 NumPy,请查看我们的 Introduction to Data Science with Python 课程。

pandas 中的切片

pandas 将切片扩展到 DataFrame 和 Series,提供了更直观的方式访问和操作结构化数据。在继承 Python 基本切片的同时,pandas 也针对表格数据引入了独特行为。

对 DataFrame 的行和列进行切片

import pandas as pd  

# Create a sample DataFrame
student_data = {'Name': ['Alice', 'Bob', 'Charlie'], 
                'Age': [25, 30, 35], 
                'Score': [85, 90, 95]}

students = pd.DataFrame(student_data

# Slice the first two rows
print(students[:2])  
# Output:
#      Name  Age  Score
# 0   Alice   25     85
# 1     Bob   30     90  

# Slice specific columns by name
print(students[['Name', 'Score']])  
# Output:
#       Name  Score
# 0    Alice     85
# 1      Bob     90
# 2  Charlie     95  

使用 .loc[] 和 .iloc[]

pandas 提供 .loc[](基于标签)和 .iloc[](基于位置),用于更精确的控制。

# Slice using .iloc (index-based)
print(df.iloc[1:, 1:])  
# Output:
#    Age  Score
# 1   30     90
# 2   35     95  
# Slice using .loc (label-based)
print(df.loc[:1, ['Name', 'Age']])  
# Output:
#    Name  Age
# 0  Alice   25
# 1    Bob   30  

pandas 切片的关键差异

  • 区间包含性:与 Python 切片不同,.loc[] 对行和列的停止索引是包含的。

  • 混合索引:pandas 允许同时使用标签和位置进行切片,提供更大的灵活性。

若需进阶技巧,请探索我们的 Manipulating DataFrames with pandas 课程。

不同数据类型中的 Python 切片

接下来我们通过不同数据类型的示例来看看切片如何在不同数据结构上工作,比如字符串、列表、元组,或更复杂的数据类型。

字符串切片

在 Python 中,字符串是字符序列,这意味着您可以像对列表或元组那样对其进行切片。这使得切片在提取子串、反转文本或格式化数据时很有用。这里我使用 :。

Basic string slicing# Extract a substring
text = "Python Slicing"
print(text[7:14])  # Output: Slicing  

# Reverse a string
print(text[::-1])  # Output: gnicilS nohtyP  

slice() 函数允许您创建可复用的切片对象,以便进行一致的切片操作。

# Create a slice object
slicer = slice(7, 14)

# Apply the slice object
print(text[slicer])  # Output: Slicing  

实用用例

  • 数据解析:从结构化文本(如 CSV 行或日志文件)中提取特定字段。
  • 文本处理:通过移除前后缀或不需要的字符来格式化字符串。

列表和元组切片

列表和元组是 Python 的基础数据结构,对它们进行切片可以简化您的工作。

基础列表切片

# Extract elements from a list
numbers = [10, 20, 30, 40, 50]

print(numbers[1:4])  
# Output: [20, 30, 40]  

# Skip elements using steps
print(numbers[::2])  
# Output: [10, 30, 50]  

元组切片

元组是不可变的,但仍然可以通过切片提取其中的一部分。

# Slice a tuple
colors = ("red", "green", "blue", "yellow")

print(colors[:2])  
# Output: ('red', 'green')  

过滤与重排

切片是过滤或重排元素的便捷方式。

# Filter odd-indexed elements
data = [5, 10, 15, 20, 25]

filtered = data[1::2]  
# Output: [10, 20]  

数据分析中的切片

在数据分析中,切片有助于提取和重组数据子集以进一步检视。常见用例如 子集选择,即提取相关的行或列以便聚焦分析;以及 数据转换,即为更好的可视化或报告重组数据集。

结合 NumPy 的切片

import numpy as np  

# Create a 2D array
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# Extract the first two rows
print(data[:2, :])  
# Output:
# [[1 2 3]
#  [4 5 6]]  

关于 NumPy 的内容还有很多,但您可以在我们的 Data Structures and Algorithms in Python 课程中深入探索 NumPy 切片。

结合 pandas 的切片

另一种选择是 pandas,它提供了访问和操作行列的切片功能。下面快速演示如何在 DataFrame 中切片行和列:

import pandas as pd  

# Create a DataFrame
df = pd.DataFrame({
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Age': [25, 30, 35],
    'Score': [85, 90, 95]
})

# Slice rows and columns
print(df.loc[:1, ['Name', 'Score']])  
# Output:
#      Name  Score
# 0   Alice     85
# 1     Bob     90  

高级 Python 切片技巧

掌握更高级的技巧后,您可以以惊人的精度和效率操作数据结构。以下技巧有助于您编写更简洁、更高效的 Python 代码。

用切片修改列表

切片的一项不太为人所知的能力是可以直接修改列表。您可以在一行代码中替换、插入或删除多个元素。

替换元素:

numbers = [1, 2, 3, 4, 5]
numbers[1:4] = [20, 30, 40]

print(numbers)
# Output: [1, 20, 30, 40, 5]

插入元素:

numbers = [1, 2, 5]
numbers[2:2] = [3, 4]  

print(numbers)
# Output: [1, 2, 3, 4, 5]

删除元素:

numbers = [1, 2, 3, 4, 5]
numbers[1:4] = []

print(numbers)
# Output: [1, 5]

内存效率

在对数据结构进行切片时,尤其是面对大型数据集,了解 Python 是创建拷贝还是视图至关重要。请注意,对内置的列表和字符串,切片总是创建拷贝;而对NumPy 数组,切片会创建视图,即原数组和切片指向相同的内存数据。

在下面的代码中,切片操作创建的是原 NumPy 数组的视图而非拷贝。这意味着 arr 和 sliced 都引用同一块底层内存。因此,修改 sliced 会直接影响 arr。

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
sliced = arr[1:4]
sliced[0] = 99

print(arr)
# Output: [1, 99, 3, 4, 5]

在外部库中修改切片时务必谨慎,避免产生意外的副作用。

考虑特殊情况

切片功能强大,但也并非万无一失。以下是一些常见陷阱:

越界切片

numbers = [1, 2, 3]

print(numbers[5:10])
# Output: []

越界切片不会抛出错误——它只会返回一个空列表。

空切片

numbers = [1, 2, 3]

print(numbers[1:1])
# Output: []

当 start 与 stop 索引相同,结果是空切片。

令人迷惑的步长行为

numbers = [1, 2, 3, 4, 5]

print(numbers[::-2])
# Output: [5, 3, 1]

步长 -2 会反向切片,并跳过每隔一个元素。

负索引

负索引允许您从序列的末尾开始进行切片,而不是从开头。

访问最后的元素

numbers = [1, 2, 3, 4, 5]

print(numbers[-1])  # Last element
# Output: 5

反转列表

numbers = [1, 2, 3, 4, 5]

print(numbers[::-1])  # Reverse the list
# Output: [5, 4, 3, 2, 1]

在序列长度未知或需要倒序处理数据时,负索引尤其有用。

多维切片

切片并不限于一维结构。在诸如 NumPy 的库中,您可以精确地对多维数组进行切片。

import numpy as np

matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# Extract a sub-matrix
sub_matrix = matrix[:2, 1:]

print(sub_matrix)
# Output:
# [[2, 3],
#  [5, 6]]

多维切片小贴士:

  1. 使用逗号分隔每个维度的切片。

  2. 避免以产生歧义的方式混用负索引和默认切片:。

调试提示:在调试多维切片时,务必打印中间切片,确保目标子集正确。

常见的 Python 切片错误

Python 切片功能强大,但也难免犯错。一些常见陷阱甚至会绊倒有经验的开发者。了解这些问题及其解决方案,对编写干净、准确的代码至关重要。

产生 off-by-one(差一)错误

当切片操作中的起止索引错误地包含或排除了某些元素时,就会出现差一错误。这是切片中最常见的问题之一,可能导致结果不准确或代码出错。

numbers = [1, 2, 3, 4, 5]

# Intending to extract [2, 3, 4]
slice_result = numbers[1:3]

print(slice_result)
# Output: [2, 3]

这里,停止索引会排除索引为3的元素,导致意外遗漏。如何避免该错误:

  • 理解停止索引规则:切片中的停止索引是不包含的,意味着它不会出现在结果中。如果希望包含该元素,请在停止索引上加 1。
  • 使用明确的长度:如果切片应包含特定范围的索引,请确认起止索引的计算与意图一致。

这是更正后的示例:

numbers = [1, 2, 3, 4, 5]
slice_result = numbers[1:4]

print(slice_result)
# Output: [2, 3, 4]

误解步长值

步长用于决定切片时在索引间跳过多少元素。对步长理解不当会导致出乎意料的结果,尤其是在使用负步长时。

跳过过少或过多元素

numbers = [1, 2, 3, 4, 5]
# Expecting every second element

step_result = numbers[::3]

print(step_result)
# Output: [1, 4]

这里,步长 3 跳过了太多元素,导致结果数量少于预期。下面是一个关于负步长令人困惑的示例。

numbers = [1, 2, 3, 4, 5]
step_result = numbers[::-2]

print(step_result)
# Output: [5, 3, 1]

负步长会在反向的同时跳过元素,但若不了解其行为,难以预测结果。以下是我在正确使用步长时会考虑的几点:

  • 规划好间隔:确保步长与您需要的间隔匹配。若需要每隔一个元素取值,请使用step 为 2。

  • 单独测试负步长:用小例子试验,确认您理解反向切片的行为。

  • 结合起止索引使用步长:配合起止索引限制切片范围,同时应用步长。

下面是改进步长用法的示例:

Every second element:
numbers = [1, 2, 3, 4, 5]

print(numbers[::2])
# Output: [1, 3, 5]

Reverse with a step:
numbers = [1, 2, 3, 4, 5]

print(numbers[4::-2])
# Output: [5, 3, 1]

未遵循最佳实践

以下是一些避免出错的好建议:

  1. 可视化您的切片:使用注释或打印输出来验证切片逻辑。

  2. 多做示例练习:从小而简单的列表开始,练习理解 start、stop、step 参数。

  3. 为提高清晰度使用内置函数:如有不确定,用 slice() 函数创建切片以实现更明确的逻辑。

总结

在本指南中,我们探讨了以下内容:

  • 切片语法与参数的基础知识,包括 start、stop 和 step。

  • 诸如通过切片修改列表等有趣技巧,同时关注内存效率实践以及对多维数据的处理。

  • 人们常犯的错误,比如差一错误和对步长的误解,以及如何避免它们。

巩固理解的最佳方式是动手实践。尝试真实世界的数据集,测试边界情况,并通过超越基础的练习来挑战自己。

DataCamp 随时为您提供帮助。试试我们的 Data Manipulation with Python 课程,以及 Developing Python Packages 等课程,获得互动式学习体验。无论您是初学者,还是希望精进技巧,这些资源都能帮助您掌握切片与其他技能。

Oluseye Jeremiah

作者

Oluseye Jeremiah

专注于人工智能、机器学习与数据科学的技术写作者,擅长将复杂概念讲解得清晰易懂。

主题