Python - 传值是如何工作的?

13

我有一个关于Python中函数调用的问题。假设我想编写一个名为 superLongFunc(expr) 的函数。这个函数非常长,很难调试。我想将函数拆分成更小的助手函数,以提高可读性,例如 smallFunc1(expr),smallFunc2(expr) 等。

我的问题是,这是否会影响代码的性能?Python 中函数调用的工作原理是什么?Python 是否通过引用将变量传递给函数?还是在将其提供给函数之前,Python 会复制变量呢?

我知道这可能是一个比较初级的问题,但它一直困扰着我。谢谢!


1
还有一些经验法则可以解决这个问题。timeitdis模块是其中之一。有时候能够自己测试一下也是很有用的。 - Joel Cornett
1个回答

16

Python使用一种有时称为按对象传递的系统。在向函数传递参数时不会复制任何内容。函数参数的名称在函数体内局部绑定,绑定到与函数调用中提供的相同对象。

这与大多数人们所认为的“按值传递”不同,因为它不会复制对象。但它也不同于“按引用传递”,因为引用是指向对象而不是指针——一个新的名称被绑定,但是绑定到相同的对象上。这意味着您可以改变传入的对象,但是在函数内重新绑定名称对函数外部没有影响。一个简单的例子说明了这种差异:

>>> def func(x):
...     x[0] = 2 # Mutating the object affects the object outside the function
>>> myList = [1]
>>> func(myList)
>>> myList # myList has changed
[2]
>>> def func(x):
...     x = 2 # rebinding name has no effect outside the function
>>> myList = [1]
>>> func(myList)
>>> myList # myList is unaffected
[1]

我简单的理解是,Python中赋值给裸名字(即形如“name = value”语句)与其他所有操作都完全不同。唯一一个只对名称而非值进行操作的方法是执行“name = value”操作。(当然,也有像瞎搞“globals()”等细枝末节的例外情况,但这些都是危险的领域。)特别地,“name = value”与“obj.prop = value”、“obj[0] = value”、“obj += value”这些看起来像赋值但实际上操作的是对象而非名称的表达式都不同。

话虽如此,Python中函数调用本身就存在一定的成本(例如为执行帧设置上下文等)。如果一个函数被多次调用,这种成本可能会导致明显的性能影响。因此将一个函数拆分成多个仍然可能会产生性能影响,因为每个额外的函数调用都会增加一些开销。


3
你所描述的只是按值调用,其中的值是对象的引用。 - Marcin
2
两个例子的区别在于第一个是对x所指向的对象进行操作,而第二个则是对x本身这个名称进行操作(即将其赋值为一个新对象)。这就是为什么第二个没有任何效果。 - BrenBarn
1
@larsmans C++中的按引用传递通常是指在函数中进行赋值操作会影响调用上下文中变量的值。事实上,这里有证据:http://ideone.com/rW3Hl 在Python中你不能这样做(除非你将locals()字典实际传递给函数)。 - Marcin
4
这句话与类型无关。形如“name = value” 的赋值语句会重新绑定变量名,其他所有操作都针对变量的值进行。 - BrenBarn
1
@larsmans 如果你愿意,你可以称之为按引用传递,但是这样做会使你使用该术语的含义与其他大多数人使用该术语时所指的含义完全不同。 - Marcin
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接