先来看这一段程序:

a = [1,2,3,4]

b = a

a[0] = 100

print(b)

输出结果是:

[100, 2, 3, 4]

看上去很简单,但我发现一些教科书、Python课程,以及同行上课时并不会提到这样的例子,更不用说解释为什么了。Python中所有的变量,都是指针。非计算机专业的编程学习者对变量是指针还是实体这个问题不敏感,如果不和他们讲清楚这一点,碰到类似上面那样的程序,他们是没法理解的,也许他们就会被类似的问题困扰一辈子。本着对Python初学者人生负责的态度,我觉得这个问题需要强调一下。

更广泛地说,可以把Python中所有可赋值的东西,都称为变量。所有的变量,都是指针。比如列表的元素是可赋值的,因此列表的元素就是指针。字典的值部分也是可赋值的,所以字典的值也是指针。指针的本质是内存地址。指针这个词太专业了,不好,不如称作箭头,一个指向内存单元中存放的数据的箭头。这么说,变量就是箭头,对变量进行赋值,就是将该箭头指向内存中的某处。当然其它程序设计语言中的变量,未必是上述的情况。

a = 3

b = 4

上面两条赋值语句的效果,可以理解为下图: 

指针网络pytorch代码 python有指针概念吗_指针网络pytorch代码

a是个箭头(指针),指向内存中某处存放的3,b也一样,它指向4。

用一个变量对另一个变量进行赋值,就是让两个变量指向相同的地方。因此,若再执行:

a = b

产生的效果如下:

指针网络pytorch代码 python有指针概念吗_指针 是否相同_02

a指向了b指向的地方,所以a的值也变成4。我们说变量a的值是4,归根到底是在说, a指向4。

Python中有两个运算符,"is"和"==",含义有所不同,但有些类似。a is b为True,说的是a和b指向同一个地方; 而 a == b 为True,说的是a和b指向的地方的内容相同,但a和b 未必指向同一个地方。例如:

1.  a = [1,2,3,4] #a指向列表 [1,2,3,4]
2.  b = [1,2,3,4] #b指向另一个列表 [1,2,3,4]
3.  print( a == b) #True
4.  print( a is b) #False
5.  c = a
6.  print( a == c)  #True
7.  print( a is c)  #True
8.  a[2] = "ok"
9.  print(c)     #[1, 2, 'ok', 4]

上面程序执行完第5行时,效果如下图:

指针网络pytorch代码 python有指针概念吗_赋值_03

内存中有两份列表[1,2,3,4],a和b分别指向它们。因此a和b指向不同的地方,但是它们指向的地方存放的内容是一样的。故第3行输出True而第4行输出False。第5行使得c与a指向同一份列表。因此第6、7行都输出True。

第8行,修改了a[2],情况变为下图:

指针网络pytorch代码 python有指针概念吗_数据_04

  因为c和a指向同一个地方,所以a的内容变了,c的内容自然也变。所以输出c,结果就是 [1, 2, 'ok', 4]。

对int,float,complex, str,tuple类型的变量a和b,只需关注 a == b是否成立,一般不需要关注 a is b是否成立。因这些数据本身都不会更改,不会产生上面第8行那样,a指向的内容改了b指向的内容也跟着变的情况。

对 list,dict,set类型的变量a和b, a == b和 a is b的结果都需要关注。因这些数据本身会改变。改变了a指向的内容,说不定b指向的内容也变了。

因为列表的元素可以被赋值,因此,列表的元素其实也是箭头。

a = [1,2,3,4]

b = [1,2,3,4]

上面这两条语句,准确的效果如下图:

指针网络pytorch代码 python有指针概念吗_指针网络pytorch代码_05

       a和b的每个元素,如a[0],b[1],都是箭头。a[0]和b[0]没有分别指向不同的两个1,是因为1本身不可变,没有必要保有两份。若对a[0]进行赋值,那就是让a[0]指向了别处,而不是将a[0]所指向的那个1改成别的什么。所以,假如a[0]被赋成别的值,b[0]并不会受影响,它仍然指向1。

Python函数的参数也是箭头。Python函数的参数(形参),是实参(实际调用时给的参数)的拷贝。拷贝的意思是,形参和实参指向同一个地方。对形参赋值(让其指向别处)不会影响实参。例如:

1.  def Swap(x,y):
2.      tmp = x
3.      x = y
4.      y = tmp
5.  a,b = 4,5
6.  Swap(a,b)
7.  print(a,b)  #输出 4, 5

进入Swap函数时,x等于a,y等于b。Swap函数执行过程中交换了x,y的值,但这并不会影响a和b。在函数中的 tmp = x 刚执行完时,效果如下:

指针网络pytorch代码 python有指针概念吗_Python_06

x,y分别是a和b的拷贝,即x和a同指向4,y和b同指向5。tmp = x使得tmp也指向4。Swap函数执行完后,x和y的值交换了,本质上是说x和y交换了它们的指向,因此情况变成下图:

指针网络pytorch代码 python有指针概念吗_赋值_07

显然,a和b的指向不会发生任何变化,它们的值自然不变。

但是如果函数执行过程中,改变了形参所指向的地方的内容,则实参所指向的地方内容也会被改变。例如:

1.  def Swap(x,y):
2.      tmp = x[0]
3.      x[0] = y[0]
4.      #若x,y是列表,则x[0],y[0]都是箭头
5.      y[0] = tmp
6.  a = [4,5]
7.  b = [6,7]
8.  Swap(a,b)
9.  #进入函数后,x和a指向相同地方,
10. #y和b指向相同地方
11. print(a,b)  #>>[6, 5] [4, 7]

这个程序中,Swap(a,b)使得a和b的下标为0的元素发生了交换。这是因为,x和a指向同一张列表[4,5],y和b指向同一张列表[6,7]。因此x[0]就是a[0],y[0]就是b[0]。进入Swap函数,执行完tmp = x[0] 时,情况如下:

指针网络pytorch代码 python有指针概念吗_指针 是否相同_08

Swap交换了x[0]和y[0],也就交换了a[0]和b[0]。因此该函数执行完时,情况如下:

指针网络pytorch代码 python有指针概念吗_指针网络pytorch代码_09

由于a和x指向相同的地方,所以x[0]变了,a[0]自然也变了。b和y的关系亦然。

函数的返回值也是箭头。假设函数中的返回语句是 return x, 如果x是变量,则返回值和x指向相同的地方;如果x是一个非变量的表达式,那么返回值指向这个表达式计算出来后的值。可赋值的东西都是箭头,但是箭头未必都可赋值。例如函数的返回值,就是不可赋值的。比如f是个无参数的函数,f()的返回值(如果有的话)就是箭头。a = f() 是用f的返回值对a进行赋值,使得a和f的返回值指向同一个地方。但f()= 100 这种写法是不可行的。