从Python中链表的节点删除思考Python自定义对象的可更改性

在leetcode上做到 203. 移除链表元素的题目,该题目的难度为简单,但是在做的时候,由于对Python中的可更改对象和不可更改对象概念理解不是很深刻,导致看不懂答案。该题的一种解法如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        head_v = ListNode(0)
        head_v.next = head  # 设置一个虚拟头结点,方便操作
        node = head_v  # 创建一个head_v对象的引用,后面对该引用进行操作,达到不改变head_v结点指针的同时更改head_v链表里的内容
        
        while node.next:
            if node.next.val == val:
                node.next = node.next.next
            else:
                node = node.next
        
        return head_v.next

关于该问题的解法,本文不多做阐述,感兴趣的可以去leetcode看相关解答,我们这里关注点在于,为何对node链表进行遍历和修改,head_v链表的内容会跟着改变,但head_v指向的地址没有改变
在python中不存在所谓的传值调用,一切传递的都是对象的引用,也可以认为是传址。Python中自带的数据格式对应的对象分为可更改对象和不可更改对象,不可更改对象包括int、字符串(string)、float、(数值型number)、元组(tuple);可更改对象主要指list和dict。对于这二者的区别,本文不多阐述,感兴趣自行百度,这里我们思考自定义的对象的可更改性
在上面的代码中head_v是一个ListNode对象,head_v的成员变量next也是一个ListNode对象,另一个成员变量val是一个int类型。我们创建head_v对象的一个引用node,当我们改变node的成员变量时,head_v会跟着改变吗?按照该题解答的逻辑,head_v应该是会跟着变的。我们来验证一下:

#!/usr/bin/python
class node:
	def __init__(self, val=0, list_=[], next=None):
		self.val = val
		self.list_ = list_
		self.next = next
		
node1 = node(val=1, list_=[1,1])
node2 = node(val=2, list_=[2,2])
node3 = node(val=3, list_=[3,3])

node1.next = node2
node2.next = node3
node1_cp = node1  # 创建node1的引用
print(node1)
print(node1_cp)  # 引用的地址与node1相同
print(node1.next)  # 该地址和node2的地址相同
print(node1_cp.next)  # 引用的对象类型成员变量地址与node1的对应成员变量地址相同
print(node2)
print(node3)
# 3个node各自地址互不相同

#修改node1的成员变量,node1_cp的也会相应改变,自定义成员变量都是可更改的
node1.val = -1  # 即使是int类型的成员变量也是可更改的
node1.list_.append(1)
print(node1_cp.val)
print(node1_cp.list_)

# 修改node1本身
node1=node2  # 该操作相当于将node1的地址修改为了node2的地址
print(node1)  # 此时node1地址已经修改为node2地址
print(node1_cp)  # 但node1_cp的地址没有随之改变
print(node1_cp.next)  # 仍是node2的地址
# 由此可见对象引用的成员变量是可更改的,但是对象引用本身是不可更改的,类比于list对象:
a = [1,2,3]
b = a
c=  [4,5,6]
b.append(4)
print(a,b)  # [1, 2, 3, 4] [1, 2, 3, 4]
b = c
print(a,b)  # [1, 2, 3, 4] [4, 5, 6]
# list中的每个元素相当于对象里的成员变量, list对象本身地址改变时,其引用的地址不会随之改变

# 所以,node1的地址改变了,不影响node1_cp仍指向头结点。且对新node1的修改,也即对node2的修改,也即对node1_cp.next的修改,因为三者都是指向node2对象的地址。

所以该题中,对head_v的引用node本身的地址的修改(node=node.next)不会影响head_v本身地址的改变,node地址变成了node.next的地址,但node.next地址与head_v中的head_v.next的地址仍然是相同的,所以对node.next中的成员变量修改,仍然会对应到head_v.next中,除非node.next本身被赋值成其他地址。

总结:
1. 一个自定义对象是可更改的,自定义对象引用的成员变量被修改时,该自定义对象的成员变量也会被修改。
2. 当自定义对象引用本身地址被修改时,其引用的对象会维持原来地址。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值