Python学习笔记

3.25

今天遇到了这样一个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

def set(self, L):
self=ListNode(val=L[0])
now = self
for x in L[1:]:
now.next = ListNode(val=x)
now = now.next

def print(self):
now = self
while (now != None):
print('%d ' % (now.val))
now = now.next
print('\n')

a=ListNode()
a.set([1,2,3])
a.print() # 输出0

可见,a.set()并没有改变a的值,不得其解

看了这篇文章

豁然开朗,之前理解有误的原因是自动把C++的赋值与Python混淆了

Python的变量,其实是C++意义上的指针

下面摘录几段:


构造函数返回指针

Python 的构造函数将构造匿名对象,且返回此对象的一个指针。

这是 Python 与指针的第一个重要联系。

用代码描述,对于Python代码:

1
sampleNum = 0

其不类似于 C++ 代码:

1
int sampleNum = 0;

而更类似于:

1
2
3
4
int __tmpNum = 0, *sampleNum = &__tmpNum;

// 或者:
shared_ptr<int> sampleNum(new int(0));

__setitems__操作将隐式解指针

Python 与指针的另一个重要联系在于 Python 的隐式解指针行为。

虽然 Python 不存在显式解指针操作,但(有且仅有)__setitems__操作将进行隐式解指针,通过此方法对变量进行修改等同于通过解指针操作修改变量原值。

此种性质意味着:

  1. 任何不涉及__setitems__的操作都将成为指针重绑定。

对于Python代码:

1
2
3
4
numList = [None] * 10

# Rebinding
numList = [None] * 5

其相当于:

1
2
3
4
5
6
int *numList = new int[10];

// Rebinding
delete[] numList;
numList = new int[5];
delete[] numList;

由此可见,对 numList 的非__setitems__操作,导致 numList 被绑定到了一个新指针上。

  1. 任何涉及__setitems__的操作都将成为解指针操作。

由于 Python 对哈希表的高度依赖,“涉及__setitems__的操作”在 Python 中实际上是一个非常广泛的行为,这主要包括:

  • 对数组的索引操作
  • 对哈希表的查找操作
  • 涉及__setattr__的操作(由于 Python 将 attribute 存储在哈希表中,所以__setattr__操作最终将是某种__setitems__操作)

于是,将set()改为这样:

1
2
3
4
5
6
7
def set(self, L):
self.val = L[0] # self=ListNode(val=L[0])
self.next = None
now = self
for x in L[1:]:
now.next = ListNode(val=x)
now = now.next

就解决了问题

self是默认将实例本身传入的参数,根据上文,实际传入的是实例的指针,而self=ListNode(val=L[0])self重新绑定至一个新建的节点,那么后续进行的操作就与原实例无关了