【问题标题】:Python - how does passing values work?Python - 传递值如何工作?
【发布时间】:2018-05-25 11:54:11
【问题描述】:

我有一个关于 Python 中的函数调用的问题。假设我想编写一个名为 superLongFunc(expr) 的函数。该函数超长且很难调试。我想将函数拆分为更小的辅助函数以获得更好的可读性,例如 smallFunc1(expr)、smallFunc2(expr) 等。

我的问题是,这会影响代码的性能吗?调用函数在 Python 中究竟是如何工作的? Python 是否通过引用将变量传递给函数?还是在将变量提供给函数之前复制变量?

我知道这是一个非常棘手的问题,但它一直困扰着我一段时间。提前致谢!

【问题讨论】:

  • 也有经验的方法来解决这个问题。我想到了timeitdis 模块。有时,能够自己测试一下是很有用的。

标签: python function styles


【解决方案1】:

Python 使用有时称为call-by-object 的系统。将参数传递给函数时不会复制任何内容。函数参数的名称在函数体内本地绑定到函数调用中提供的相同对象。

这与大多数人认为的“按值调用”不同,因为它不会复制对象。但它也不同于“通过引用调用”,因为引用是对 object 的引用——绑定了一个新的 name,但对同一个对象。这意味着您可以改变传入的对象,但在函数内部重新绑定名称对函数外部没有影响。一个简单的区别示例:

>>> 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]

我的简单思考方式是分配给一个裸名——即name = value 形式的语句——与 Python 中的其他所有内容完全不同。 唯一 对名称而不是对值进行操作的方法是 name = value。 (这也有一些挑剔的例外,比如乱搞globals() 等等,但无论如何这些都是危险的领域。)特别是name = valueobj.prop = valueobj[0] = valueobj += value 和其他类似的不同看起来像赋值但实际上作用于对象而不是名称的东西。

也就是说,Python 中的函数调用本身就有一定的开销(用于设置执行框架等)。如果一个函数被多次调用,这种开销会导致明显的性能影响。因此,将一个函数拆分为多个函数仍然会对性能产生影响,因为每个额外的函数调用都会增加一些开销。

【讨论】:

  • 您所描述的只是按值调用,其中值是对对象的引用。
  • 两个例子的区别在于第一个对x指向的object进行操作,而第二个对name进行操作> x 本身(通过将其分配给新对象)。这就是为什么第二个没有效果的原因。
  • @larsmans C++ call-by-reference 是通常的call-by-reference 类型,也就是说函数中的赋值会影响调用上下文中变量的值。事实上,证据如下:ideone.com/rW3Hl 在 python 中你不能这样做(除非你实际上将 locals() dict 传递给函数)。
  • @Jimster:当您尝试x += 1 时,会在对象上查找方法__iadd__(就地添加)。在list 的情况下,会找到并调用该方法。然后它会失败,因为不允许将整数添加到列表中。 (它适用于x += [1]。)如果找不到__iadd__,则表达式将“重写”为x = x + 1,因此会调用__add__x = x + 1 重新绑定名称。
  • 与类型无关。 name = value 形式的分配重新绑定名称。其他一切都基于价值观。
猜你喜欢
  • 2021-06-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
相关资源
最近更新 更多