繁体   English   中英

python pandas dataframe,是传值还是传引用

[英]python pandas dataframe, is it pass-by-value or pass-by-reference

如果我把一个dataframe传给function,在function内部修改,是传值还是传引用?

我运行以下代码

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
    df = df.drop('b',axis=1)
letgo(a)

a的值在 function 调用后没有改变。 这是否意味着它是按值传递?

我也试过以下

xx = np.array([[1,2], [3,4]])
def letgo2(x):
    x[1,1] = 100
def letgo3(x):
    x = np.array([[3,3],[3,3]])

事实证明letgo2()确实改变了xxletgo3()没有。 为什么会这样?

简短的回答是,Python总是按值传递,但每个Python变量实际上都是指向某个对象的指针,因此有时它看起来像是传递引用。

在Python中,每个对象都是可变的或不可变的。 例如,列表,dicts,模块和Pandas数据帧是可变的,并且int,字符串和元组是不可变的。 可以在内部更改可变对象(例如,将元素添加到列表中),但不可变对象不能。

正如我在开始时所说,您可以将每个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的整个内部范围分配一些东西。

如果你绝对必须创建一个全新的对象并让它在外部可见(有时候是pandas的情况),你有两个选择。 '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得到了改变。 (我通常将全局变量用于脚本中许多函数使用的共享参数,但我不允许它们改变那些全局变量。)

问题不是PBV与PBR。 这些名称只会导致像Python这样的语言混淆; 它们是为像C语言一样的语言或像Fortran(作为典型的PBV和PBR语言)发明的。 Python总是按值传递,但这并不具有启发性。 这里的问题是价值本身是否发生了变化,或者你是否获得了新的价值。 熊猫通常在后者的一边犯错。

http://nedbatchelder.com/text/names.html很好地解释了Python的名称系统。

为了加入@Mike Graham的答案,他指出了一个非常好的读物:

在您的情况下,重要的是要记住名称之间的区别。 adfxxx都是名称 ,但它们在示例的不同点引用相同或不同的

  • 在第一个示例中, letgo df letgo 绑定到另一个值,因为df.drop返回一个新的DataFrame除非您设置参数DataFrame inplace = True请参阅doc )。 这意味着名称dfletgo函数的本地),它指的是a的值,现在引用一个新值,这里是df.drop返回值。 a指的值仍然存在且未更改。

  • 在第二个例子中, letgo2 改变了 x ,没有重新绑定它,这就是letgo2修改xxletgo2 不像之前的例子,这里的本地名称x始终是指名义值xx指的是,改变在地方价值,这就是为什么值xx指的是已经改变。

  • 在第三个示例中, letgo3 x letgo3 绑定到新的np.array 这导致名称x ,本地为letgo3 ,之前引用xx的值,现在引用另一个值,即新的np.array xx指的是没有改变。

Python既不是通过值传递也不是通过引用传递。 这是通过作业。

支持参考,Python FAQ: https//docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

IOW:

  1. 如果传递不可变值,则对其进行的更改不会更改其在调用方中的值 - 因为您要将名称重新绑定到新对象。
  2. 如果传递一个可变值,则在被调用函数中进行的更改也会更改调用者中的值,只要您不将该名称重新绑定到新对象即可。 如果重新分配变量,创建新对象,则在调用者中看不到该更改以及对名称的后续更改。

因此,如果您传递一个列表并更改其第0个值,则在被调用者和调用者中都会看到该更改。 但是,如果使用新列表重新分配列表,则此更改将丢失。 但是,如果你切列表,并替换一个新的列表,这种变化被认为是在两个被叫和主叫方。

例如:

def change_it(list_):
    # This change would be seen in the caller if we left it alone
    list_[0] = 28

    # This change is also seen in the caller, and replaces the above
    # change
    list_[:] = [1, 2]

    # This change is not seen in the caller.
    # If this were pass by reference, this change too would be seen in
    # caller.
    list_ = [3, 4]

thing = [10, 20]
change_it(thing)
# here, thing is [1, 2]

如果您是C粉丝,您可以将其视为按值传递指针 - 而不是指向值的指针,只是指向值的指针。

HTH。

这是drop的文档:

返回删除了请求轴中的标签的新对象。

因此创建了一个新的数据帧。 原来没变。

但是对于python中的所有对象,数据框通过引用传递给函数。

你需要在函数的开头创建'a'全局,否则它是一个局部变量,并且不会更改主代码中的'a'。

简短回答:

  • 按值: df2 = df.copy()
  • 通过引用: df2 = df

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM