我有一个函数test
,它接受一个DataFrame并向其追加数据。我希望更改放入函数的全局变量。我有以下脚本:
import pandas as pd
global dff
def test(df):
df = df.append({'asdf':1, 'sdf':2}, ignore_index=True)
return(df)
dff = pd.DataFrame()
test(dff)
在此之后,dff
仍为空;它没有被编辑。但是,如果你这样做:
import pandas as pd
def test(df):
df['asdf'] = [1,2,3]
return(df)
dff = pd.DataFrame()
test(dff)
dff
会在[1,2,3]
列下有'asfd'
。请注意,我甚至不必将变量声明为global
。
为什么会这样?
我实际上想知道,因为第二个我认为我理解变量工作空间,我被证明是错误的,而且我厌倦了不断遇到这个BS *
我知道问题的解决方案是:
import pandas as pd
def test(df):
df = df.append({'asdf':1, 'sdf':2}, ignore_index=True)
return(df)
dff = pd.DataFrame()
dff = test(dff)
但我真的只想弄清楚为什么初始方法不起作用,特别是考虑到我已经展示的第二个脚本。
*显然它不是完整的BS,但经过3年的休闲编程后我无法理解它
答案 0 :(得分:2)
我在PyCon 2015上发现了一个非常好的演讲,解释了我试图在下面解释的内容,但是使用的图表使其更加清晰。我将在下面留下解释来解释原始的3个脚本是如何工作的,但我建议你去观看视频:
Ned Batchelder - Facts and Myths about Python names and values - PyCon 2015
所以,我想我已经弄清楚上面两个脚本中发生了什么。我会尝试分解它。如果需要,请随时纠正我。
几条规则:
变量是指向实际保存数据的基础对象的链接/指针的名称。例如,街道地址。街道地址不是房子;它只是指向一所房子。所以地址(101 Streetway Rd。)是指针。在GPS中,您可能将其标记为“主页”。 “Home”这个词本身就是变量。
函数适用于对象,而不是变量或指针。将变量传递给函数时,实际上是传递对象,而不是变量或指针。继续这个房子的例子,如果你想在房子里添加一个套牌,你希望装饰承包商在房子上工作,而不是元物理地址。
函数中的return
命令返回指向对象的指针。所以这就是房子的地址,而不是房子或你称之为房子的名字。
=
是一个函数,意思是“指向此对象”。 =
前面的变量是输出,右边的变量是输入。这将是命名房屋的行为。因此Home = 101 Streetway Rd.
使变量Home
指向101 Streetway Rd上的房子。假设你搬进了邻居家,这是102 Streetway Rd。这可以通过Home = Neighbor's House
来完成。现在,Home
现在是指针102 Streetway Rd。
这里我将使用--->
来表示“指向”
在我们开始编写脚本之前,让我们从我们想要的开始。我们希望变量指向的对象objdff
(没有global dff
因为没有做任何相关的事情)
import pandas as pd def test(df): df = df.append({'asdf':1, 'sdf':2}, ignore_index=True) return(df) dff = pd.DataFrame() test(dff)
让我们来看看这个功能。在我们到达之前没有任何有趣的事情发生:
dff = pd.DataFrame()
在这里,我们将变量dff
分配给由pd.DataFrame
创建的对象,这是一个空数据帧。我们将此对象称为objdff
。所以在这一行的最后,我们有dff ---> objdff
。
下一行:test(dff)
函数处理对象,因此我们说我们将在test
指向的对象dff
上运行函数objdff
。这将我们带到了函数本身。
def test(df):
在这里,我们实际上是一个=
函数。传递给测试函数objdff
的对象由函数变量df
指向。现在df --->objdff
和 dff---> objdff
转到下一行:df = df.append(...)
让我们从df.append(...)
开始吧。 .append(...)
会传递到objdff
。这使对象objdff
运行一个名为'append'的函数。正如@Jai所指出的,.append(...)
方法使用return
命令输出一个全新的DataFrame,该DataFrame附加了数据。我们将调用新对象objdff_apnd
。
现在我们可以转到df = ...
部分了。我们现在拥有的基本上是df = objdff_apnd
。这很简单。变量df
现在指向对象objdff_apnd
。
在这一行的最后,我们有df ---> objdff_apnd
和dff ---> objdff
。这就是问题所在。我们想要的对象(objdff_apnd
)没有被dff
指向。
最后,变量dff
仍然指向objdff
,而不是objdff_apnd
。这将我们带到脚本3(见下文)。
import pandas as pd def test(df): df['asdf'] = [1,2,3] return(df) dff = pd.DataFrame() test(dff)
就像脚本1 dff ---> objdff
一样。在test(dff)
期间,函数变量df ---> objdff
。这是事情不同的地方。
再次将操作(?)df['asdf'] = [1,2,3]
发送到基础对象objdff
。上一次,这导致了一个新对象。但是,这次['asdf']
操作会直接编辑对象objdff
。因此,对象objdff
中包含额外的“asdf”列。
因此,最后我们有df ---> objdff
和dff ---> objdff
。因此,它们指向同一个对象,这意味着变量dff
指向已编辑的对象。
一旦我们突破该函数,变量dff
仍然指向objdff
,其中包含新数据。这给了我们想要的结果。
import pandas as pd def test(df): df = df.append({'asdf':1, 'sdf':2}, ignore_index=True) return(df) dff = pd.DataFrame() dff = test(dff)
此脚本与脚本1完全相同,但dff = test(dff)
除外。我们将在一秒钟内完成。
从脚本1的结尾开始,我们在函数test(dff)
结束时向右移动,我们有dff ---> objdff
和df ---> objdff_apnd
。
函数test
具有return
命令,因此返回对象objdff_apnd
。这会将行dff = test(dff)
转换为dff = objdff_apnd
。
因此,最后,我们有dff ---> objdff_apnd
,这正是我们想要的结果。
答案 1 :(得分:1)
append
会返回一个新对象,因此它不会填充原始数据帧。 检查此列表示例:
def test1(a):
a.append(1)
def test2(a):
a = [1, 2, 3]
def test3(a):
a[0] = 10
aa = list()
test1(aa)
print(aa)
aa = list()
test2(aa)
print(aa)
aa = list([1])
test3(aa)
print(aa)
输出:
[1]
[]
[10]
append
功能:DataFrame.append(other, ignore_index=False, verify_integrity=False, sort=None)[source]
Append rows of other to the end of this frame, returning a new object. Columns not in this frame are added as new columns.
append
返回新对象global
关键字的方式是错误的...我认为即使您在第一个脚本中没有global
,它仍然没有任何区别......我没有关于global
关键字的详细信息,所以我不会提及它的任何内容..但我知道如何使用关键字,这绝对不是正确的使用方式