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

编写修改列表的函数的Python方式?,python,Python,在python中,函数参数通过对象引用传递。 这意味着修改列表的最简单代码将修改对象本身 a = [1,2,3] def remove_one(b): b.remove(1) remove_one(a) print(a) 方法remove\u one不返回任何内容。如果这是一个公共方法,那么必须假设它将修改对象,并且在方法内部不是线程安全的 第二种方法是如下构造代码段: a = [1,2,3] def remove_one(b): b.remove(1) ret

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

a = [1,2,3]

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

remove_one(a)
print(a)
方法
remove\u one
不返回任何内容。如果这是一个公共方法,那么必须假设它将修改对象,并且在方法内部不是线程安全的


第二种方法是如下构造代码段:

a = [1,2,3]

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

print(remove_one(a[:]))
在这里,未修改的内容将返回一个新列表。这使方法调用方承担了很多责任


然后再次
列表理解
,修改列表内容的python方法总是创建一个新对象

a = [1,2,3]

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

print(remove_one(a))

我不清楚是否有一种“pythonic”的方法可以做到这一点,因此我将做出一些假设来接受挑战:

  • 更安全的假设是,接受可变对象的函数将修改其内容
  • 列表理解不直接修改对象是有原因的。我只是不知道
  • 编写使用列表理解来修改可变参数的函数的“pythonic”方法并不清楚

在我看来,你并不是在寻找一个简单的元素删除,而是一个更复杂的列表变异。但是,如果您是,只需调用
.delete()
即可

关于更复杂的突变,一个很好的例子是标准库中的
heapq
模块。他们做得很差。让我们看看

导入heapq
q=[5,6,3]
heapq.heapify(q)#这突变了q
a=heapq.heapop(q)#这也是
如果您必须知道,堆队列算法会将列表中最低的项放在第一位。所以
q
现在是
[5,6]
a
3

这与您所拥有的类似,它位于标准库中。不管你怎么想

然而,我强烈反对这一惯例。对
heapq
模块函数的所有调用都需要将
q
作为第一个参数传递,有时
q
会发生变异。但是
q
本身永远不会返回

这正是类要解决的问题。那为什么要逃课呢

导入heapq
HeapQ类(列表):
定义初始化(self,a_列表):
super().uuu init_uuu(一个列表)#调用基'list()`
heapq.heapify(自我)
def pop(自我):
返回heapq.heappop(自身)
def推送(自身、值):
heapq.heappush(自身,值)
调用约定更符合逻辑

q=HeapQ([5,6,3]) >>>a=q.pop() >>>q [5, 6] >>>a 3. 这是一种更具python风格的方式,并且没有太多的惊喜。
q
上的突变并不奇怪,每个突变都是对
q
的调用
q.pop()
q.push(0)
,两者都发生变异
q


实际上,在我需要
heapq
模块的时候,我一直在做一个更详细的版本。

TLDR:如果修改一个参数,不要返回它


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

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

def remove_one(b):
    b.remove(1)  # modify, do not return
如果不修改参数而是创建一个副本,则必须
返回该副本才能使操作有意义:

def one_removed(b):
    c = b.copy()  # create new object...
    c.remove(1)
    return c      # ...and return it
函数/方法名称通常反映它们是否主动修改其参数。修饰功能倾向于使用主动动词,而非修饰功能倾向于使用被动动词或形容词


无论是否修改参数,都没有“pythonic”的选择——对原始或副本进行操作是两种根本不同的操作。根据用例的不同,两者都是有效的

>>> items = [4, 3, 10, 2, 5]
>>> sorted(items)
[2, 3, 4, 5, 10]
>>> items.sort()
>>> items
[2, 3, 4, 5, 10]
一般来说,在Python中变异参数更快,而创建新结果更容易推理。 具体来说,理解表示操作()——它们的设计目的不是修改它们的iterable



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

您不必使用列表理解,但是如果您不想修改对象,您必须自己创建一个新的,但是最后一段代码与其他两段代码不相等,因为它将删除列表中所有出现的
1
,而不是第一段,正如
remove
所做的那样。因此,您可能只需调用
您的\u列表。当您想从列表中删除某些内容时,请删除(1)
,而不是将其包装到另一个函数中。这就是为什么您要向函数中添加文档字符串,以便调用方通过读取文档知道将发生什么。通常,您只需命名您的函数来表示它的作用,
sorted()
vs
list.sort()
是如何区分这两种方法(返回新值vs变异对象状态)的一个很好的例子。要使用列表理解并“就地”更改列表,您可以使用一种技巧,用新值替换参数列表中的所有元素,例如
b[:]=[num for num in b if num!=-1]
IMHO更改可变参数已经不是pythonic(显式优于隐式)。制作b的副本(
local\u b=b.copy()
),更改(
local\u b.remove(1)
)并返回它看起来更加明确