问题描述
我们都知道全局变量不好的教条.当我开始学习 python 时,我读取传递给函数的参数被视为函数内部的局部变量.这似乎至少是事实的一半:
We all know the dogma that global variables are bad. As I began to learn python I read parameters passed to functions are treated as local variables inside the funktion. This seems to be at least half of the truth:
def f(i):
print("Calling f(i)...")
print("id(i): {}
".format(id(i)))
print("Inside f(): i += 1")
i += 1
print("id(i): {}".format(id(i)))
return
i = 1
print("
Before function call...")
print("id(i): {}
".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 中函数的调用机制是通过对象引用调用".这意味着参数最初是由它的对象引用传递的,但是如果在函数内部修改它,则会创建一个新对象变量.在我看来,为了避免函数无意修改全局变量的设计,这似乎是合理的.
As I read now, the calling mechanism of functions in Python is "Call by object reference". This means an argument is initially passed by it's object reference, but if it is modified inside the function, a new object variable is created. This seems reasonable to me to avoid a design in which functions unintendedly modify global variables.
但是如果我们将列表作为参数传递会发生什么?
But what happens if we pass a list as an argument?
def g(l):
print("Calling f(l)...")
print("id(l): {}
".format(id(l)))
print("Inside f(): l[0] += 1")
l[0] += 1
print("id(l): {}".format(id(l)))
return
l = [1, 2, 3]
print("
Before function call...")
print("id(l): {}
".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
如我们所见,对象引用保持不变!所以我们处理一个全局变量,不是吗?
As we can see, the object reference remains the same! So we work on a global variable, don't we?
我知道我们可以通过将列表的副本传递给函数来轻松克服这个问题:
I know we can easily overcome this by passing a copy of the list to the function with:
g(l[:])
但我的问题是:在 Python 中实现函数参数的两种不同行为的原因是什么?如果我们打算操作一个全局变量,我们也可以像对待整数一样使用global"关键字来表示列表,不是吗?这种行为如何与python显式优于隐式"的禅宗一致?
But my question is: What is the reason the implement two different behaviors of function parameters in Python? If we intend to manipulate a global variable, we could also use the "global"-keyword for list like we would do for integers, couldn't we? How is this behavior consistent with the zen of python "explicit is better than implicit"?
推荐答案
Python 有两种类型的对象 - 可变的和不可变的.大多数内置类型,如 int、string 或 float,都是不可变的.这意味着他们无法改变.像 list、dict 或 array 这样的类型是可变的,这意味着它们的状态可以改变.几乎所有用户定义的对象也是可变的.
Python has two types of objects - mutable and inmutable. Most of build-in types, like int, string or float, are inmutable. This means they cannot change. Types like list, dict or array are mutable, which means that their state can be changed. Almost all user defined objects are mutable too.
当您执行i += 1
时,您为i 分配了一个新值,即i + 1
.这不会以任何方式改变 i,它只是说它应该忘记 i 并将其替换为 i + 1
的值.然后 i
被一个全新的对象替换.但是当你在列表中执行 i[0] += 1
时,你对列表说应该用 i[0] + 1
替换元素 0.这意味着 id(i[0])
将被新对象改变,列表 i 的状态将改变,但它的身份保持不变——它是同一个对象,只是改变了.
When you do i += 1
, you assign a new value to i, which is i + 1
. This doesn't mutate i in any way, it just says that it should forget i and replace it with value of i + 1
. Then i
becomes replaced by a completely new object.But when you do i[0] += 1
in list, you say to the list that is should replace element 0 with i[0] + 1
. This means that id(i[0])
will be changed with new object, and the state of list i will change, but it's identity remains the same - it's the same object it was, only changed.
请注意,在 Python 中,这对于字符串而言并非如此,因为它们是不可变的,更改一个元素将使用更新的值复制字符串并创建新对象.
Note that in Python this is not true for strings, as they are immutable and changing one element will copy the string with updated values and create new object.
这篇关于Python:为什么是 int &列表函数参数区别对待?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!