
在python中,变量并非直接存储值,而是存储对内存中对象的引用。当我们执行a = [1, 2, 3]时,变量a指向内存中的一个列表对象。当我们将这个列表作为参数传递给函数时,例如func(a),函数内部的形参(比如nums1)也会指向同一个列表对象。这意味着在函数内部通过nums1对列表对象进行的任何修改,都会反映到函数外部的原始列表a上。
然而,这里的关键在于“修改对象的内容”与“重新赋值变量”之间的区别。
原始问题中遇到的困惑,正是源于对这一区别的误解。让我们来看一下问题中提供的代码片段:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
if m + n == m:
nums1 = nums2 if m == 0 else nums1
else:
# 问题所在:这里对 nums1 进行了重新赋值
nums1 = nums1[:m]
nums1.extend(nums2) # 此时的 nums1 已经是新的列表对象
print('nums1', nums1) # 打印的是新的局部 nums1
print('nums2', nums2)
nums1.sort() # 对新的局部 nums1 进行排序在这段代码中,当执行到nums1 = nums1[:m]时,会发生以下情况:
因此,尽管函数内部的print('nums1', nums1)显示了预期的合并排序结果,但当函数执行完毕,外部调用者所持有的nums1引用依然指向那个未被修改的原始列表。
立即学习“Python免费学习笔记(深入)”;
要实现真正的原地修改,我们必须操作传入的原始列表对象的内容,而不是让局部变量指向一个新的列表。主要有两种正确的方法:
切片赋值是Python中一种强大的特性,它允许你替换列表的全部或部分内容,而无需创建新的列表对象。
from typing import List
def merge_in_place_slice(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
将 nums2 合并到 nums1 中,并原地修改 nums1。
假设 nums1 的长度足以容纳合并后的所有元素。
"""
# 1. 获取 nums1 的有效部分和 nums2 的有效部分
temp_list = nums1[:m] + nums2[:n]
# 2. 对合并后的临时列表进行排序
temp_list.sort() # 或者使用 sorted(temp_list)
# 3. 将排序后的结果赋值回 nums1 的切片
# 这一步是关键,它修改了原始 nums1 对象的内容
nums1[:] = temp_list
# 示例用法
nums1_test = [1,2,3,0,0,0]
m_test = 3
nums2_test = [2,5,6]
n_test = 3
print(f"修改前 nums1: {nums1_test}")
merge_in_place_slice(nums1_test, m_test, nums2_test, n_test)
print(f"修改后 nums1: {nums1_test}")
# 预期输出:
# 修改前 nums1: [1, 2, 3, 0, 0, 0]
# 修改后 nums1: [1, 2, 2, 3, 5, 6]nums1[:] = temp_list 这行代码的含义是:将 temp_list 中的所有元素逐一替换掉 nums1 中从头到尾的所有元素。这样,nums1 仍然是原来的那个列表对象,但其内容已经被完全更新了。
如果你需要分步操作,可以使用列表的clear()方法清空原始列表,然后使用extend()方法添加新元素。
from typing import List
def merge_in_place_methods(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
将 nums2 合并到 nums1 中,并原地修改 nums1。
假设 nums1 的长度足以容纳合并后的所有元素。
"""
# 1. 获取 nums1 的有效部分和 nums2 的有效部分
temp_list = nums1[:m] + nums2[:n]
# 2. 对合并后的临时列表进行排序
temp_list.sort()
# 3. 清空原始 nums1 的内容
nums1.clear()
# 4. 将排序后的元素添加到原始 nums1 中
nums1.extend(temp_list)
# 示例用法
nums1_test = [1,2,3,0,0,0]
m_test = 3
nums2_test = [2,5,6]
n_test = 3
print(f"修改前 nums1: {nums1_test}")
merge_in_place_methods(nums1_test, m_test, nums2_test, n_test)
print(f"修改后 nums1: {nums1_test}")
# 预期输出与方法一切片赋值相同这两种方法都确保了对传入的原始列表对象进行了修改,符合“原地修改”的要求。
虽然问题明确要求“原地修改”,但有时为了代码的简洁性和灵活性,或者在某些场景下,返回一个新的列表会是更合适的选择。在提供的答案中,merge函数实际上就是采用了这种方式:
from typing import List
def merge_and_return_new_list(nums1: List[int], m: int, nums2: List[int], n: int) -> List[int]:
"""
合并两个列表并返回一个新的排序后的列表。
"""
# 直接创建并合并有效部分
new_list = nums1[:m] + nums2[:n]
# 对新列表进行排序并返回
return sorted(new_list)
# 示例用法
nums1_orig = [1,2,3,0,0,0]
m_orig = 3
nums2_orig = [2,5,6]
n_orig = 3
result_list = merge_and_return_new_list(nums1_orig, m_orig, nums2_orig, n_orig)
print(f"原始 nums1 (未改变): {nums1_orig}")
print(f"新合并列表: {result_list}")
# 预期输出:
# 原始 nums1 (未改变): [1, 2, 3, 0, 0, 0]
# 新合并列表: [1, 2, 2, 3, 5, 6]这种方法简单直接,但它不会修改原始的nums1。调用者需要接收函数的返回值来获取合并后的列表。如果函数签名要求-> None,则表示期望进行原地修改;如果要求-> List[int],则表示期望返回一个新的列表。理解这些约定对于编写和阅读Python代码至关重要。
在Python中对列表进行原地修改时,关键在于理解变量是对象的引用。直接对函数形参进行重新赋值会导致局部变量指向新的对象,从而无法影响函数外部的原始列表。要实现真正的原地修改,应通过操作原始列表对象的内容来完成,例如使用切片赋值nums1[:] = ...或列表的clear()和extend()等方法。理解这些细微差别对于编写正确、高效且符合预期的Python代码至关重要。
以上就是Python函数中列表原地修改的深度解析:理解变量赋值与对象操作的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号