在 Python 中,虽然没有显式的指针概念(如 C/C++ 中的 int* p
),但所有变量本质上都是对对象的引用(类似于指针的抽象)。理解这一点对掌握 Python 的内存管理、参数传递和可变/不可变对象的行为至关重要。
当你在 Python 中赋值一个变量时,变量名实际上是一个指向对象的引用(内存地址的抽象)。例如:
a = 10
b = a
a
和 b
都指向同一个整数对象 10
的内存地址。
使用 id(a)
和 id(b)
可以验证它们指向同一地址。
a = 10
b = a
print("\n", hex(id(a)))
print("\n", hex(id(b)))
print("\n", id(a) == id(b)) # 输出 True
int, str, tuple
)无法在原地修改。重新赋值会创建新对象:a = 5
a += 1 # 新对象 6 被创建,a 指向新地址
不可变对象 - 常数 int
示例
num1 = 11
num2 = num1
print("Before value is updated:")
print("num1 = ", num1, "num1内存地址", hex(id(num1)))
print("num2 = ", num2, "num2内存地址", hex(id(num2)))
num1 = 22
print("\nAfter value is updated:")
print("num1 = ", num1, "num1内存地址", hex(id(num1)))
print("num2 = ", num2, "num2内存地址", hex(id(num2)))
list, dict, set
)可以在原地修改,所有引用它的变量都会“看到”变化:list1 = [1, 2, 3]
list2 = list1
list2.append(4)
print(list1) # 输出 [1, 2, 3, 4]
可变对象 - 字典 dict
示例
dict1 = { 'value': 11 }
dict2 = dict1
print("Before value is updated:")
print("dict1 = ", dict1, "dict1内存地址", hex(id(dict1)))
print("dict2 = ", dict2, "dict2内存地址", hex(id(dict2)))
dict1 = { 'value': 22 }
print("\nAfter value is updated:")
print("dict1 = ", dict1, "dict1内存地址", hex(id(dict1)))
print("dict2 = ", dict2, "dict2内存地址", hex(id(dict2)))
虽然 Python 没有显式指针,但可以通过以下方式模拟类似行为:
方法 1:使用可变容器包裹值
def modify_value(wrapper):
wrapper[0] += 1 # 修改容器内的值
value = [10] # 用列表包裹整数
modify_value(value)
print(value[0]) # 输出 11
方法 2:使用 ctypes
模块(低级操作,慎用)
import ctypes
# 定义一个整数指针
value = ctypes.c_int(10)
p = ctypes.pointer(value)
p.contents.value = 20
print(value.value) # 输出 20
方法 3:自定义类封装值
class Pointer:
def __init__(self, value):
self.value = value
p = Pointer(10)
p.value = 20
print(p.value) # 输出 20
Python 的函数参数传递本质上是“传递对象引用”:
def func(x):
x += 1 # 新对象,不影响外部
a = 10
func(a)
print(a) # 输出 10
def func(lst):
lst.append(4) # 原地修改
my_list = [1, 2, 3]
func(my_list)
print(my_list) # 输出 [1, 2, 3, 4]
空指针(Null Pointer)
在 Python 中,空指针对应的是 None
,表示“没有指向任何对象”。
所有变量在未赋值时默认指向 None
,类似于 C/C++ 的 NULL
或 nullptr
。
示例:
p = None # 空指针
if p is None:
print("p 是空指针")
# 对象销毁后引用变为 None
a = [1, 2, 3]
b = a
del a # 删除 a 的引用,但 b 仍指向原对象
del b # 删除最后一个引用,对象被回收
双指针(Double Pointer)
双指针通常用于需要通过间接引用修改对象的场景(例如修改指针的指向)。Python 中可以通过以下方式模拟:
方法 1:使用可变容器(如列表)
通过列表包裹值,间接修改内部的值:
def change_pointer(ptr):
ptr[0] = "new value" # 修改指针指向的值
# 用列表模拟指针
pointer = ["old value"]
change_pointer(pointer)
print(pointer[0]) # 输出 "new value"
方法 2:自定义类封装指针
通过类属性模拟指针的间接操作:
class Pointer:
def __init__(self, value):
self.value = value
p1 = Pointer(10)
p2 = p1 # p2 和 p1 指向同一个对象
p2.value = 20 # 修改值
print(p1.value) # 输出 20
双指针的典型应用场景
双指针在算法和数据结构中非常常见,例如:
(1) 链表操作
反转链表时,通过双指针(快慢指针)修改节点指向:
class ListNode:
def __init__(self, val=0):
self.val = val
self.next = None
# 反转链表(双指针法)
def reverse_list(head):
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
(2) 数组遍历
左右指针处理有序数组(如两数之和):
def two_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
sum_val = nums[left] + nums[right]
if sum_val == target:
return [left, right]
elif sum_val < target:
left += 1
else:
right -= 1
return []
(3) 树结构操作
在二叉树中修改子树结构时,需要双指针传递父节点和子节点的引用:
class TreeNode:
def __init__(self, val=0):
self.val = val
self.left = None
self.right = None
def delete_node(root, key):
if not root:
return None
if root.val == key:
# 通过双指针重新连接子树
if not root.right:
return root.left
if not root.left:
return root.right
# 找到右子树的最小节点
min_node = root.right
while min_node.left:
min_node = min_node.left
root.val = min_node.val
root.right = delete_node(root.right, min_node.val)
elif root.val > key:
root.left = delete_node(root.left, key)
else:
root.right = delete_node(root.right, key)
return root
总结
空指针:用 None
表示未指向任何对象。
双指针:通过可变容器或自定义类间接操作对象。
应用场景:链表、数组、树结构操作等需要间接修改引用的场景。
Python 使用引用计数和垃圾回收机制管理内存:
当对象的引用计数为 0 时,内存会被运行的Garbage Collection
自动回收。
避免循环引用(如两个对象互相引用),否则可能导致内存泄漏。
共享状态:多个对象通过引用同一可变对象共享数据。
函数副作用:通过传递可变对象让函数修改外部状态。
高效内存使用:避免复制大对象(如传递列表而非创建新副本)。