简而言之,Python总是按值传递,但每个Python变量实际上都是指向某个对象的指针,因此有时看起来像按引用传递。
在Python中,每个对象都是可变的或不可变的。e、 例如,list、dict、modules和Pandas数据帧是可变的,int、string和tuple是不可变的。可变对象可以在内部更改(例如,向列表中添加元素),但不可变对象不能。
正如我在开始时所说的,您可以将每个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) # will alter a
在某些情况下,您可以完全清空原始变量并用新数据重新填充它,而无需实际执行直接赋值,例如,这将更改v指向的原始对象,这将更改稍后使用v时看到的数据:def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v) # will alter v
注意,我不是直接给x赋值;而是给x的整个内部范围赋值。
如果必须创建一个全新的对象并使其在外部可见(熊猫有时也是如此),则有两个选项。“clean”选项只是返回新对象,例如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() # will alter a!
直接改变全局变量通常是个坏主意,因为任何读过您的代码的人都很难弄清楚a是如何改变的。(我通常将全局变量用于脚本中许多函数使用的共享参数,但我不允许它们更改这些全局变量。)