编写修改列表的函数的Python方式?
在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
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()
vslist.sort()
是如何区分这两种方法(返回新值vs变异对象状态)的一个很好的例子。要使用列表理解并“就地”更改列表,您可以使用一种技巧,用新值替换参数列表中的所有元素,例如b[:]=[num for num in b if num!=-1]
IMHO更改可变参数已经不是pythonic(显式优于隐式)。制作b的副本(local\u b=b.copy()
),更改(local\u b.remove(1)
)并返回它看起来更加明确