我想编写一个Python函数来改变其中一个参数(这是一个列表,即可变)。像这样:
def change(array):
array.append(4)
change(array)
我更熟悉传递价值而不是Python的设置(无论你决定调用它)。所以我通常会写这样一个函数:
def change(array):
array.append(4)
return array
array = change(array)
这是我的困惑。由于我可以改变参数,第二种方法似乎是多余的。但第一个感觉不对。此外,我的特定功能将有几个参数,其中只有一个会改变。第二种方法清楚地说明了哪个参数正在改变(因为它被赋值给变量)。第一种方法没有给出任何指示。有会议吗?哪个更好'?谢谢。
答案 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)