Pythonic 方式编写修改列表的函数?

The*_*eer 6 python

在Python中,函数参数是通过对象引用传递的。这意味着修改列表的最简单代码将修改对象本身。

a = [1,2,3]

def remove_one(b):
    b.remove(1)

remove_one(a)
print(a)
Run Code Online (Sandbox Code Playgroud)

该方法remove_one不返回任何内容。如果这是一个公共方法,则必须假设它将修改对象并且在方法内不是线程安全的。


第二种方法是像这样构建代码片段:

a = [1,2,3]

def remove_one(b):
    b.remove(1)
    return(b)

print(remove_one(a[:]))
Run Code Online (Sandbox Code Playgroud)

这里 a 的内容没有被修改,并返回一个新列表。这给方法调用者带来了很多责任。


话又说回来list comprehensions,修改列表内容的Pythonic方法总是创建一个新对象。

a = [1,2,3]

def remove_one(b):
    b = [num for num in b if b!=1]
    return(b)

print(remove_one(a))
Run Code Online (Sandbox Code Playgroud)

我不清楚是否有一种“Pythonic”方式来做到这一点,所以我会做出一些假设来挑战:

  • 假设采用可变对象的函数会修改其内容会更安全
  • 列表推导式不直接修改对象是有原因的。我只是不知道
  • 编写使用列表推导式修改可变参数的函数的“Pythonic”方式尚不清楚

Mis*_*agi 3

TLDR:如果修改参数,请勿返回它。


list.sort()对列表进行适当排序。为了提醒您这一事实,它不会返回排序列表。

设计和历史常见问题解答

惯用的做法是通过不返回参数来指示是否修改参数。例如,您的第一个案例是正确的,您的第二个案例则不正确:

def remove_one(b):
    b.remove(1)  # modify, do not return
Run Code Online (Sandbox Code Playgroud)

如果您不修改参数但创建副本,则return该副本必须使操作有意义:

def one_removed(b):
    c = b.copy()  # create new object...
    c.remove(1)
    return c      # ...and return it
Run Code Online (Sandbox Code Playgroud)

函数/方法名称通常反映它们是否主动修改其参数。修饰功能倾向于使用主动动词,而非修饰功能倾向于使用被动动词或形容词。


没有“Pythonic”选择是否修改参数 - 对原始或副本进行操作是两种根本不同的操作。两者都有效,具体取决于用例。

>>> items = [4, 3, 10, 2, 5]
>>> sorted(items)
[2, 3, 4, 5, 10]
>>> items.sort()
>>> items
[2, 3, 4, 5, 10]
Run Code Online (Sandbox Code Playgroud)

一般来说,在 Python 中改变参数速度更快,而创建新结果更容易推理。具体来说,推导式代表函数式编程操作(mapfilter)——它们被设计为不修改其可迭代对象。


是否因修改而返回参数仅与该参数有关。例如,list.pop修改列表并且不返回它——但是,它确实返回弹出的元素。