【问题标题】:Python: Handling exceptions while sortingPython:在排序时处理异常
【发布时间】:2014-03-25 20:01:51
【问题描述】:

我有一个需要根据key function 排序的对象列表。问题是我列表中的某些元素在列表排序时可能会“过时”。当对此类过期项目调用 key 函数时,它会失败并出现异常。

理想情况下,我想要一种使用键函数对列表进行排序的方法,这样当在元素上调用键函数时发生错误时,该元素会从排序结果中排除。

我的问题可以用下面的例子来重构:假设我有两个类,GoodBad

class Good(object):
    def __init__(self, x):
        self.x = x
    def __repr__(self):
        return 'Good(%r)' % self.x

class Bad(object):
    @property
    def x(self):
        raise RuntimeError()
    def __repr__(self):
        return 'Bad'

我想根据它们的x 属性对这些类的实例进行排序。例如:

>>> sorted([Good(5), Good(3), Good(7)], key=lambda obj: obj.x)
[Good(3), Good(5), Good(7)]

现在,当我的列表中有Bad 时,排序失败:

>>> sorted([Good(5), Good(3), Bad()], key=lambda obj: obj.x)
... RuntimeError

我正在寻找一个神奇的函数func,它根据键函数对列表进行排序,但只是忽略键函数引发错误的元素:

>>> func([Good(5), Good(3), Bad()], key=lambda obj: obj.x)
[Good(3), Good(5)]

实现这一点的最 Pythonic 方式是什么?

【问题讨论】:

  • Bad.x 是否真的在被要求时提出 RuntimeError 还是只是提出 AttributeError
  • 我很好奇你的对象为什么会过期。它是另一个线程,在您排序时修改您的对象吗?还是它们真的会因为时间的流逝而过期?但是,为什么这会使属性引发异常?例如,将属性 Apple.age 提升为 TooOldException 是不明智的。
  • @Kevin,对象过期是因为它们代表打开的浏览器窗口(=另一个进程)中的(Selenium)网络元素。如果例如。页面在列表排序时重新加载,然后列表中的至少一些元素在访问其属性时引发 Selenium StaleElementReferenceExceptions

标签: python list sorting


【解决方案1】:

我知道的每个排序算法都不会因为它们已经过时或其他原因而丢弃一些值。排序算法的任务是对列表进行排序,并快速排序,其他的都是无关的、特定的任务。
所以,我会自己写这个神奇的功能。它将分两步进行排序:首先它会过滤列表,只留下Good 值,然后对结果列表进行排序。

【讨论】:

  • 我明白你的意思,但分两步排序不是一种选择。看来GoodBad 的例子太做作了。正如我在问题的第一段中所述,列表项在排序时可以从Good 变为Bad。排序前清除所有Good项,因此不能保证后续排序不会出现异常。
  • @MichaelHerrmann 我明白了。好吧,就像 dstrmoberg 建议的那样,唯一的选择是自己编写具有此特定功能的排序算法。
【解决方案2】:

我曾经用合并排序做过一次。合并排序使消除不再有用的值变得相对简单。

我参与的项目位于 http://stromberg.dnsalias.org/~dstromberg/equivalence-classes.html#python-3e 。随意突袭它的想法,或从中提取代码;它像语音一样免费(GPLv2 或更高版本,由您选择)。

该代码中的排序应该几乎可以满足您的要求,除了它将具有重复项的列表排序为列表列表,其中每个子列表具有相同的值。那部分可能对你有用,也可能没用。

我在http://stromberg.dnsalias.org/svn/sorts/compare/trunk/ 有一个更简单的归并排序(它不做重复的桶的事情,但它也不处理丢弃不再好的值)。该文件是 .m4 文件,但不要被它骗了 - 它实际上是从同一个 .m4 文件自动生成的纯 python 或 cython。

【讨论】:

    【解决方案3】:

    由于 key 函数的结果会随着时间而改变,并且大多数排序实现可能假定一个确定性的 key 函数,最好每个对象只执行一次 key 函数,以确保排序良好且无崩溃的最终结果列表。

    def func(seq, **kargs):
        key = kargs["key"]
        stored_values = {}
        for item in seq:
            try:
                value = key(item)
                stored_values[item] = value
            except RuntimeError:
                pass
        return sorted(stored_values.iterkeys(), key=lambda item: stored_values[item])
    
    
    print func([Good(5), Good(3), Bad()], key=lambda obj: obj.x)
    

    结果:

    [Good(3), Good(5)]
    

    【讨论】:

    • 这是个好主意,但它会从列表中删除重复项:good_5 = Good(5) ; print func([good_5, Good(3), good_5, Bad()], key=lambda obj: obj.x)。尽管如此,我还是会沿着这些思路走。谢谢!
    • 仅此而已:可能不是一个好主意。您可能会隐藏比预期更多的异常。
    【解决方案4】:

    如果列表项在排序时可以从好到坏,那么您无能为力。 keys 在排序前只计算一次,因此键的任何变化对排序函数都是不可见的:

    >>> from random import randrange
    >>> values = [randrange(100) for i in range(10)]
    >>> values
    [54, 72, 91, 73, 55, 68, 21, 25, 18, 95]
    >>> def k(x):
    ...   print x
    ...   return x
    ... 
    >>> values.sort(key=k)
    54
    72
    91
    73
    55
    68
    21
    25
    18
    95
    

    (如果在排序过程中多次评估键,您会看到多次打印的数字。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-14
      • 2017-01-24
      • 1970-01-01
      • 1970-01-01
      • 2014-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多