Python的:为什么INT和列表功能参数区别对待?(Python: Why are int & l

2019-10-21 05:36发布

我们都知道,教条全局变量是不好的。 当我开始学习Python我读传递给函数的参数作为的Funktion内局部变量处理。 这似乎是真理的至少一半:

def f(i):
    print("Calling f(i)...")
    print("id(i): {}\n".format(id(i)))
    print("Inside f(): i += 1")
    i += 1
    print("id(i): {}".format(id(i)))
    return

i = 1
print("\nBefore function call...")
print("id(i): {}\n".format(id(i)))
f(i)

这个计算结果为:

Before function call...
id(i): 507107200

Calling f(i)...
id(i): 507107200

Inside f(): i += 1
id(i): 507107232

正如我现在读的功能在Python中调用的机制是“打电话的对象引用”。 这意味着一个参数最初通过它的对象引用传递,但如果是在函数内部修改,创建一个新的对象变量。 这似乎是合理的,我以避免设计中的功能无意中修改全局变量。

但是,如果我们传递一个列表作为参数,会发生什么?

def g(l):
    print("Calling f(l)...")
    print("id(l): {}\n".format(id(l)))
    print("Inside f(): l[0] += 1")
    l[0] += 1
    print("id(l): {}".format(id(l)))
    return

l = [1, 2, 3]
print("\nBefore function call...")
print("id(l): {}\n".format(id(l)))
g(l)

这导致:

Before function call...
id(l): 120724616

Calling f(l)...
id(l): 120724616

Inside f(): l[0] += 1
id(l): 120724616

正如我们所看到的,对象引用保持不变! 因此,我们在全局变量工作,不是吗?

我知道我们可以很容易地通过传递列表的副本与功能解决这个问题:

g(l[:])

但我的问题是:什么是在Python中实现的功能参数的两个不同的行为的原因? 如果我们要操纵一个全局变量,我们也可以使用列表中的“全局” - 关键字就像我们会为整数做,难道不是吗? 这是如何的行为与Python的禅一致“明确优于隐式”?

Answer 1:

Python有两种类型的对象 - 可变和inmutable。 大多数内置的类型,如int,字符串或浮点数,是inmutable。 这意味着他们不能改变。 像列表,字典或数组类型是可变的,这意味着它们的状态可以改变。 几乎所有的用户定义的对象是可变的了。

当你做i += 1 ,你一个新的值赋给我,这是i + 1 。 这不会发生变异我以任何方式,它只是说,它应该忘了我,并用值来代替它的i + 1 。 然后i变成由一个完全新的对象替代。 但是,当你这样做i[0] += 1在列表中,你是应该更换滤芯0列表说i[0] + 1 。 这意味着, id(i[0])将与新的对象来改变和列表的状态,我会改变,但它的身份仍然是相同的-这是同一个对象它,只是改变了。

需要注意的是在Python这不是真正的字符串,因为他们是不可改变的,改变一个元素将复制使用更新的值的字符串,并创建新的对象。



Answer 2:

为什么INT和列表功能参数区别对待?

他们不是。 所有参数都同等对待,不分类型。

您所看到的两种情况之间不同的行为,因为你正在做不同的事情, l

首先,让我们简化+==+l = l + 1在第一种情况下,和l[0] = l[0] + 1中的第二个。 ( +=并不总是等于分配和+ ;这取决于运行时类上左侧的对象,它可以覆盖它的;但是在这里,为int s时,相当于一个分配和+ 。)亦,转让的右侧只是读的东西,是不感兴趣,所以我们就忽略它; 所以你有了:

l = something (in the first case)
l[0] = something (in the second case)

第二个是“分配给一个元素”,这实际上是对方法的调用的语法糖. __setitem__() . __setitem__()

l.__setitem__(0, something)

所以,现在你可以看到两者之间的区别 -

  • 在第一种情况下,你是分配给可变l 。 Python是通过按值,所以这对外部的代码没有影响。 分配给变量简单地使其指向一个新的对象; 它有它用来指向的对象没有影响。 如果您已经分配的东西l在第二种情况下,它也将有原来的对象没有任何影响。
  • 在第二种情况下,要调用的方法的对象指向l 。 这种方法恰好是列出了不同诱变方法等修改了该列表对象的内容,原始列表对象,向其中在传递给该方法的指针。 这是事实, int (在运行时类的l在第一种情况),恰好有没有方法被变异,但是这是除了点。

如果你做了同一件事l在这两种情况下(如果这是可能的),那么你可以期望相同的语义。



Answer 3:

这是跨越了一堆的语言很常见(红宝石,例如)。

变量本身的作用范围是功能。 但是,变量只是一个指向对象漂浮在内存的某个地方-而对象可以改变的。



Answer 4:

在Python一切都是对象,因此一切通过引用表示。 大约在Python变量最显着的一点是,它们所包含的对象,而不是对象本身的引用。 现在,当参数传递给函数,它们是通过引用传递。 因此,在函数的范围内,每一个参数被分配给自变量的参考,然后作为函数内的局部变量处理。 当你分配一个新值参数,要更改其引用的对象,所以你有一个新的对象,并给它的任何变化(即使它是一个可变对象)都不会被看到的功能在范围之外问题,反正不涉及到传递的参数。 这就是说,当你不分配一个新的参考参数,它保持控股的说法参考,并对其进行任何更改(当且仅当它是可变的)将在功能范围之外看到。



文章来源: Python: Why are int & list function parameters differently treated?