提问人:Neil Du Toit 提问时间:9/25/2014 最后编辑:mgilsonNeil Du Toit 更新时间:1/22/2022 访问量:20384
更改参数的 Python 函数的正确样式
Correct Style for Python functions that mutate the argument
问:
我想编写一个 Python 函数来改变其中一个参数(这是一个列表,即可变)。像这样的东西:
def change(array):
array.append(4)
change(array)
我更熟悉按值传递而不是 Python 的设置(无论你决定怎么称呼它)。所以我通常会写这样的函数:
def change(array):
array.append(4)
return array
array = change(array)
这是我的困惑。由于我只能改变参数,因此第二种方法似乎是多余的。但第一个感觉不对。此外,我的特定函数将具有多个参数,其中只有一个会更改。第二种方法明确了正在更改的参数(因为它已分配给变量)。第一种方法没有给出任何指示。有约定吗?哪个“更好”?谢谢。
答:
第一种方式:
def change(array):
array.append(4)
change(array)
是最惯用的做事方式。通常,在 python 中,我们希望函数要么改变参数,要么返回1。这样做的原因是,如果一个函数没有返回任何内容,那么它就非常清楚地表明,该函数必须有一些副作用才能证明它的存在(例如,改变输入)。
另一方面,如果你以第二种方式做事:
def change(array):
array.append(4)
return array
array = change(array)
你很容易遇到难以追踪的错误,一个可变的对象突然发生变化,而你没想到它会改变——“但我想做了一个副本”......change
1从技术上讲,每个函数都返回一些东西,那个 _something_
恰好是 None
...
评论
changed
changed
change
changed
change
change
sort
sorted
Python 中的约定是函数要么改变某些内容,要么返回某些内容,而不是两者兼而有之。
如果两者都有用,则通常编写两个单独的函数,其中 mutator 以主动动词 (如 ) 命名,非 mutator 以分词 (如 ) 命名。change
changed
内置函数和 stdlib 中的几乎所有内容都遵循这种模式。您调用的方法不返回任何内容。与 — 相同,但不理会其参数,而是返回一个新的排序副本。list.append
list.sort
sorted
对于一些特殊方法(例如,应该变异然后返回),以及一些显然必须有一个东西发生突变而另一个东西被返回的情况(例如),以及对于试图将 Python 用作一种特定于领域的语言的库,其中与目标域的习语保持一致比与 Python 的习语保持一致更重要(例如, 一些 SQL 查询表达式库)。像所有惯例一样,除非有充分的理由不遵守,否则会遵循这一惯例。__iadd__
self
list.pop
那么,为什么 Python 是这样设计的呢?
嗯,一方面,它使某些错误变得明显。如果你期望一个函数是非变异的并返回一个值,那么很明显你错了,因为你会得到一个错误,比如 .AttributeError: 'NoneType' object has no attribute 'foo'
这在概念上也很有意义:一个不返回任何内容的函数一定有副作用,否则为什么会有人编写它?
但还有一个事实是,Python 中的每个语句都只改变一件事——几乎总是语句中最左边的对象。在其他语言中,赋值是一个表达式,变异函数返回,你可以将一大堆突变链接到一行代码中,这使得你更难一目了然地看到状态变化,详细推理它们,或者在调试器中单步执行它们。self
当然,所有这些都是一种权衡——它使 Python 中的一些代码比 JavaScript 中的代码更冗长——但这是一种深深嵌入 Python 设计中的权衡。
评论
list.pop()
__iadd__
__iadd__
+=
既改变参数又返回参数几乎没有意义。它不仅可能会给阅读代码的人带来困惑,而且会使您容易受到可变默认参数问题的影响。如果获取函数结果的唯一方法是通过 mutated 参数,则为参数提供默认值是没有意义的。
还有第三个选项,您没有在问题中显示。与其改变作为参数传递的对象,不如复制该参数并返回它。这使它成为一个没有副作用的纯功能。
def change(array):
array_copy = array[:]
array_copy.append(4)
return array_copy
array = change(array)
评论
changed
change
sum
product
sorted
sorted
None
sort
append
sort
sorted
change
changed
来自 Python 文档:
某些操作(例如 y.append(10) 和 y.sort())会改变 对象,而表面上相似的操作(例如 y = y + [10] 和 sorted(y)) 创建一个新对象。通常在 Python 中(和 标准库中的所有情况)一种改变对象的方法 将返回 None 以帮助避免获取这两种类型的操作 困惑。因此,如果您错误地编写了 y.sort() 以为它会给出 你是 y 的排序副本,你最终会得到 None,这将 可能会导致程序生成易于诊断的错误。
但是,有一类操作,其中相同的操作 有时具有不同的类型具有不同的行为:增强的 赋值运算符。例如,+= 会更改列表,但不会更改元组,或者 ints (a_list += [1, 2, 3] 等价于 a_list.extend([1, 2, 3]) 并突变a_list,而 some_tuple += (1, 2, 3) 和 some_int += 1 创建新对象)。
基本上,按照约定,改变对象的函数或方法不会返回对象本身。
下一个:使用引用从其他对象获取人员地址
评论
list.append
list.pop
self