Python3.6中列表与numpy数组默认是全局变量吗?为何函数内表现不同?
嘿,这个问题其实是Python里可变对象和不可变对象以及变量作用域规则共同作用的结果,咱们一步步拆解清楚:
首先先看你给出的代码:
import numpy as np x=np.array([1,2]) y=[1,2] L=1 def set_L(x,y,L): x[0]+=1 y[0]+=1 L+=1 print(id(x)) print(id(y)) print(id(L))
核心概念先理清
- 可变对象:比如列表、numpy数组、字典这类,它们的内部元素可以被直接修改,修改操作不会改变变量指向的对象(也就是id不变)。
- 不可变对象:比如整数、字符串、元组这类,一旦创建就无法修改内部值,所谓的“修改”其实是创建一个新的对象,然后让变量指向这个新对象(id会改变)。
为什么x、y的id和全局变量一致?
你在函数里对x和y做的操作是x[0]+=1和y[0]+=1——这属于原地修改可变对象的内部元素,并没有给x或y这个变量重新赋值新的对象。
Python的作用域规则里,当你在函数内操作一个变量的内部元素(而不是给变量赋值)时,会先去全局作用域找这个变量对应的对象,找到后直接修改对象内容。所以函数里的x、y和全局的x、y指向的是同一个对象,自然id完全一致。
这绝对不是说列表和数组默认是全局变量,换个场景:如果你在函数里写x = np.array([3,4]),这就是给函数内的x变量重新赋值了,此时它就变成了局部变量,id会和全局的x不一样。
为什么L的id和全局变量不一样?
L是整数,属于不可变对象。你在函数里做L+=1,本质是创建了一个值为2的新整数对象,然后把函数内部的L变量指向这个新对象。
这个时候,函数里的L是一个局部变量——因为你对它做了赋值操作,Python会默认在函数内部创建一个新的局部变量,和全局的L完全是两个不同的变量,所以它们的id自然不一样。
如果想让函数里的L操作全局的那个,只需要在函数开头加global L声明,这样L+=1就会修改全局的L变量指向的对象(不过因为整数不可变,还是会创建新对象,但此时函数里的L和全局的是同一个变量)。
一句话总结
不是列表/数组默认是全局变量,而是你对它们做的是原地修改可变对象的操作,没有重新赋值变量;而对不可变的L做的是赋值操作,触发了局部变量的创建,这才导致了id的差异。
内容的提问来源于stack exchange,提问作者Liang Xiao

