简而言之,Python始终按值传递,但每个Python变量实际上都是指向某个对象的指针,因此有时它看起来像是按引用传递。
在Python中,每个对象都是可变或不可变的。例如,列表、字典、模块和Pandas数据帧是可变的,而整数、字符串和元组是不可变的。可变对象可以在内部更改(例如向列表添加元素),但不可变对象不能。
正如我在开头所说,您可以将每个Python变量视为指向对象的指针。当您将变量传递给函数时,函数内的变量(指针)始终是传入的变量(指针)的副本。因此,如果您将新内容分配给内部变量,您所做的只是更改局部变量以指向不同的对象。这不会改变指针所指向的原始对象,也不会使外部变量指向新对象。此时,外部变量仍然指向原始对象,但内部变量指向新对象。
如果要更改原始对象(仅限于可变数据类型),则必须执行一些更改对象的操作,而不是为局部变量分配完全新的值。这就是为什么letgo()
和letgo3()
不会更改外部项,而letgo2()
会更改它。
正如@ursan指出的那样,如果letgo()
使用类似这样的东西,那么它将更改df
指向的原始对象,这将更改通过全局变量a
看到的值:
def letgo(df):
df.drop('b', axis=1, inplace=True)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a)
在某些情况下,您可以完全清空原始变量并重新填充新数据,而无需进行直接赋值,例如这将更改
v
指向的原始对象,这将在以后使用
v
时更改所看到的数据。
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v)
请注意,我并没有直接将某个值分配给x
;而是将某个值分配给x
的整个内部范围。
如果您确实必须创建一个全新的对象,并使其在外部可见(这在pandas中有时是必需的),您有两个选择。"干净"的选择是只返回新对象,比如:
def letgo(df):
df = df.drop('b',axis=1)
return df
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)
另一种选择是在函数之外直接修改全局变量。这将更改a
指向一个新对象,任何后续引用a
的函数都将看到这个新对象:
def letgo():
global a
a = a.drop('b',axis=1)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo()
直接修改全局变量通常是一个不好的主意,因为阅读你的代码的人会很难弄清楚 a
是如何被改变的。(我通常使用全局变量来共享多个函数中使用的参数,但我不允许它们修改这些全局变量。)