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

时间:2014-09-24 22:38:14

标签: python mutable

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

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

change(array)

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

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

array = change(array)

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

3 个答案:

答案 0 :(得分:14)

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

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

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

对于某些特殊方法有一些例外(例如,__iadd__应该变异然后返回self),以及一些显然必须有一件事情的情况变异和不同的事物被返回(如list.pop),并且对于试图使用Python作为一种特定于域的语言的库,其中与目标域的习语一致更多重要的是与Python的习语(例如,一些SQL查询表达式库)保持一致。像所有惯例一样,除非有充分理由不这样做,否则遵循这一惯例。


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

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

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

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

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

答案 1 :(得分:13)

第一种方式:

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

change(array)

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

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

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

array = change(array)

当你没想到它时,你很难找到可变对象突然改变的错误 - “但我认为change制作了副本”...... < / p>

1 从技术上讲,每个函数都返回某些_something_恰好是None ... < / p>

答案 2 :(得分:1)

改变一个论点并返回它几乎没有意义。它不仅可能导致对阅读代码的人产生混淆,而且会使您容易受到mutable default argument problem的影响。如果获取函数结果的唯一方法是通过mutated参数,那么将参数赋予默认值是没有意义的。

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

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

array = change(array)