修改参数的Python函数的正确样式

Nei*_*oit 14 python mutable

我想编写一个Python函数来改变其中一个参数(这是一个列表,即可变).像这样的东西:

def change(array):
   array.append(4)

change(array)
Run Code Online (Sandbox Code Playgroud)

我更熟悉传递值而不是Python的设置(无论你决定调用它).所以我通常会写这样的函数:

def change(array):
  array.append(4)
  return array

array = change(array)
Run Code Online (Sandbox Code Playgroud)

这是我的困惑.由于我可以改变参数,第二种方法似乎是多余的.但第一个感觉不对.此外,我的特定功能将有几个参数,其中只有一个会改变.第二种方法清楚地说明了哪个参数正在改变(因为它被赋值给变量).第一种方法没有给出任何指示.有会议吗?哪个更好'?谢谢.

aba*_*ert 18

Python中的约定是函数要么改变某些东西,要么返回一些东西,而不是两者.

如果两者都有用,你通常会编写两个单独的函数,其中mutator以一个活动动词change命名,而非mutator以一个分词命名changed.

几乎所有内置函数和stdlib都遵循这种模式.list.append你正在调用的方法什么都不返回.与list.sort-but 相同但sorted仅保留其参数,而是返回一个新的已排序副本.

对于某些特殊方法有一些例外(例如,__iadd__应该变异然后返回self),并且在一些情况下,显然必须有一件事变异并且返回不同的东西(例如list.pop),并且尝试使用Python作为一种特定于域的语言的库,与目标域的习语一致,比与Python的习语(例如,一些SQL查询表达式库)一致更重要.像所有公约一样,除非有充分理由不这样做,否则遵循这一惯例.


那么,为什么Python以这种方式设计呢?

嗯,首先,它会使某些错误显而易见.如果你期望一个函数是非变异的并且返回一个值,那么很明显你错了,因为你会得到一个错误AttributeError: 'NoneType' object has no attribute 'foo'.

它也具有概念意义:一个不返回任何东西的函数必须有副作用,或者为什么有人会写它?

但是事实上,Python中的每个语句都完全改变了一件事 - 几乎总是在语句中最左边的对象.在其他语言中,赋值是一个表达式,变异函数返回self,并且您可以将一大堆突变链接到一行代码中,这使得状态变化更难以一目了然,详细了解它们,或者在调试器中逐步执行它们.

当然所有这些都是一种权衡 - 它使得一些代码在Python中比在JavaScript中更冗长 - 但它是一种权衡,深深嵌入Python的设计中.

  • 注意 `list.pop()` 可能会返回一个 (new_list, element) 对 (2认同)

mgi*_*son 15

第一种方式:

def change(array):
   array.append(4)

change(array)
Run Code Online (Sandbox Code Playgroud)

是最惯用的方式.通常,在python中,我们期望一个函数要么改变参数,要么返回1.这样做的原因是因为如果一个函数没有返回任何东西,那么它就非常清楚该函数必须具有一些副作用才能证明它的存在(例如改变输入).

另一方面,如果你以第二种方式做事:

def change(array):
  array.append(4)
  return array

array = change(array)
Run Code Online (Sandbox Code Playgroud)

当你没想到它时,你很容易找到一个可变对象突然改变的错误 - "但我以为change是副本"......

1从技术上讲,每个函数都返回一些东西,_something_恰好是None......

  • @abarnert——我没有关注那个。显然,`changed` 不是一个好名字——一个函数的名字应该更多地说明它*做什么*,而不仅仅是“这个函数改变了foo”...... (2认同)
  • 好吧,`change`并不代表任何东西......但是`changed`是`change`的过去分词,所以无论做什么改变都会做什么,除非通过制作和返回副本而不是改变 - 地点.对于一个更有用/更现实的例子,`sort`与`sorted`. (2认同)
  • 我会写我自己的答案来解释;这里还有更多要说的,但这个答案本身就很好,应该被接受。 (2认同)

Mar*_*som 6

改变一个参数并返回它几乎没有意义。它不仅可能会给阅读代码的人带来混乱,而且会让您容易受到可变默认参数问题的影响。如果获得函数结果的唯一方法是通过变异参数,则为参数提供默认值是没有意义的。

您在问题中没有显示第三个选项。不要改变作为参数传递的对象,而是复制参数并返回它。这使得它成为一个没有副作用的纯函数。

def change(array):
  array_copy = array[:]
  array_copy.append(4)
  return array_copy

array = change(array)
Run Code Online (Sandbox Code Playgroud)