Python:创建一个函数,通过引用而不是valu来修改列表

2024-06-07 04:04:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在做一些性能关键的Python工作,希望创建一个函数,如果满足某些条件,则从列表中删除一些元素。我不想创建列表的任何副本,因为它包含了很多非常大的对象。

我要实现的功能:

def listCleanup(listOfElements):
    i = 0
    for element in listOfElements:
        if(element.meetsCriteria()):
            del(listOfElements[i])
        i += 1
    return listOfElements

myList = range(10000)
myList = listCleanup(listOfElements)

我不熟悉Python的底层工作。myList是按值传递还是按引用传递?

我怎样才能让这个更快?

是否可以以某种方式扩展list类并在其中实现listCleanup()?

myList = range(10000)
myList.listCleanup()

谢谢-

乔纳森


Tags: 对象函数功能元素列表def副本range
3条回答

看起来是过早的优化。在尝试优化之前,您应该尝试更好地了解python的工作原理。

在这种情况下,您不需要担心对象的大小。复制列表是使用列表理解,或者slice只执行表面复制(复制对对象的引用,即使该术语并不真正适用于python)。但列表中的项目数可能很重要,因为del是O(n)。可能还有其他的解决方案,比如用None或传统的空对象替换一个项,或者使用另一个数据结构(如set或dictionary),这样删除项的成本要低得多。

Python以相同的方式传递所有内容,但称之为“按值”或“按引用”并不能清除所有内容,因为Python的语义与这些术语通常适用的语言不同。如果我要描述它,我会说所有传递都是按值传递的,并且该值是一个对象引用。(这就是我不想说的原因!)

如果你想从列表中筛选出一些内容,你需要建立一个新的列表

foo = range(100000)
new_foo = []
for item in foo:
    if item % 3 != 0: # Things divisble by 3 don't get through
        new_foo.append(item)

或者,使用列表理解语法

 new_foo = [item for item in foo if item % 3 != 0]

Python不会复制列表中的对象,而是foonew_foo都将引用相同的对象。(Python从不隐式复制任何对象。)


你建议你对这个操作有性能问题。从旧列表中使用重复的del语句将不会导致代码不那么惯用,处理起来更加混乱,但它将引入二次型性能,因为每次都必须重新整理整个列表。

要解决性能问题:

  • 启动并运行它。除非代码正常工作,否则您无法了解自己的性能。这也将告诉您必须优化的是速度还是空间;您在代码中提到了对这两者的关注,但通常优化需要以牺牲另一个为代价。

  • 配置文件。您可以使用the stdlib tools来及时提高性能。有各种第三方内存分析器,它们可能有些有用,但不是很好用。

  • 测量。Time或在您进行更改时重新设置内存,以查看更改是否有改进,如果有,则说明改进是什么。

  • 为了提高代码的内存敏感性,通常需要改变存储数据的方式,而不是像不构建第二个列表来进行筛选这样的微观优化。(时间也是如此,真的:改变到一个更好的算法几乎总是给最好的加速。但是,要概括速度优化是很困难的)。

    在Python中优化内存消耗的一些常见范式转换包括

    1. 使用发电机。生成器是懒惰的iterables:它们不会一次将整个列表加载到内存中,而是计算出下一个项目是什么。要使用生成器,上面的代码片段看起来像

      foo = xrange(100000) # Like generators, xrange is lazy
      def filter_divisible_by_three(iterable):
          for item in foo:
              if item % 3 != 0:
                  yield item
      
      new_foo = filter_divisible_by_three(foo)
      

      或者,使用生成器表达式语法

      new_foo = (item for item in foo if item % 3 != 0)
      
    2. 对同源序列使用numpy,特别是对数值计算的序列。这也可以加速执行大量矢量运算的代码。

    3. 将数据存储到磁盘上,如在数据库中。

在Python中,列表总是通过引用传递的。

列表中对象的大小不会影响列表的性能,因为列表只存储对对象的引用。但是,列表中的项目数确实会影响某些操作的性能,例如删除一个O(n)的元素。

如前所述,listCleanup是最坏情况的O(n**2),因为O(n)del操作在一个可能是O(n)本身的循环中。

如果元素的顺序无关紧要,则可以使用内置的set类型而不是列表。set有O(1)个删除和插入。但是,您必须确保您的对象是不可变的和散列的。

否则,你最好重新创建列表。这是O(n),你的算法至少需要O(n),因为你需要检查每个元素。您可以按如下方式将列表筛选为一行:

listOfElements[:] = [el for el in listOfElements if el.MeetsCriteria()]

相关问题 更多 >