实现与`dict.get`

时间:2017-08-08 07:23:09

标签: python list inheritance

dict有一个方便的get版本:

  

get(key[, default])

     

如果key在字典中,则返回key的值,否则返回default。 如果   默认情况下没有给出,默认为None,所以这个方法永远不会   提出KeyError

大胆强调我的,因为我看了,找不到列表的等效版本。所以我实现了自己的:

In [148]: class myList(list):
     ...:     def pop(self, idx=-1, default=None):
     ...:         try:
     ...:             return super().pop(idx)
     ...:         except IndexError:
     ...:             return default

这可以按预期工作:

In [149]: l = myList([1, 2, 3, 4, 5])

In [150]: l.pop()
Out[150]: 5

In [151]: l.pop(12345, 'default')
Out[151]: 'default'

由于这可行,因此也可以扩展到集合。无论如何,我有几个问题:

  1. 是否有一个更容易/内置/第三方等同于我所做的并不需要我延长list课程?

  2. 如果没有,有什么特别原因吗?我相信这将是一个有用的行为。

  3. 我能想到的一些特殊用例是在一个你不知道的地方,在一个你不想要的地方或者不想知道的地方打电话pop捕获错误,例如列表comp。

    This question探讨了相同的主题,但答案并不符合我的要求,正如我上面所解释的那样。

2 个答案:

答案 0 :(得分:3)

仅为字典实现的一个原因是,在最坏的情况下,检查if key in some_dict并不总是O(1)操作可能是O(n)。但即使查找是O(1),它实际上也是一项昂贵的操作,因为你需要对密钥进行散列,然后如果有匹配则需要将密钥与存储密钥进行比较以获得相等性。这使LBYL字典非常昂贵。

另一方面,对于列表,检查索引是否在边界内是一个相当便宜的操作。我的意思是你可以用一个简单的方法检查它是否在界限范围内:

valid_index = -len(some_list) <= index < len(some_list)

但这仅适用于LBYL方法。总是可以使用EAFP并捕获异常。

这应该对列表和dicts施加相同的开销。那么为什么dict采用get方法来避免异常处理呢?原因其实很简单:dict是几乎所有东西的基本构建块。大多数类和所有模块基本上只是字典,很多用户代码实际上使用字典。有一个方法可以在没有异常处理开销的情况下做到保证返回,这是(现在仍然)是值得的。

对于列表它也可能有用(我在这一点上不太确定)但我认为在大多数情况下它会抛出IndexError它意外发生,而不是故意的。因此,如果返回None s,它会隐藏真正的“错误”。

这只是我的理由。我可能是Python开发人员对当前行为有完全不同的原因。

关于你的第一个问题:

  

是否有一个更容易/内置/第三方等同于我所做的,不需要我扩展列表类?

此外,我不知道这些操作的标准库或第三方模块,列表或集的默认值。有些库实现了一个“稀疏列表”(一个填充了默认值的列表),但我看过的那些没有处理“空列表”的情况,他们使用一个默认值作为完整列表

但是,如果您不想自己进行“异常处理”,可能会有一些选项可能会很有趣:

  • 来自itertools documentation pageiter_except食谱,当发生指定的异常时停止调用函数:

    def iter_except(func, exception, first=None):
        # Copied verbatim from the above mentioned documentation page
        try:
            if first is not None:
                yield first()
            while True:
                yield func()
        except exception:
            pass
    
    >>> l = [1,2,3,4]
    >>> list(iter_except(l.pop, IndexError))
    [4, 3, 2, 1]
    
  • contextlib.suppress

    import contextlib
    def pop_with_default(lst, ind=-1, default=None):
        with contextlib.suppress(IndexError):
            return lst.pop(ind)
        return default
    
    >>> pop_with_default([1,2,3], 5, 'abc')
    'abc'
    

这里处理一般问题的另一种方法是创建一个调用另一个函数的函数,如果指定的异常将返回一个默认值,否则调用函数的结果:

def call_with_default(func, default=None, *exceptions):
    def inner(*args, **kwargs):
        try:
            res = func(*args, **kwargs)
        except exceptions:
            res = default
        return res
    return inner

>>> a = [1,2,3,4]
>>> a_pop_with_default = call_with_default(a.pop, 'abc', IndexError)
>>> [a_pop_with_default(2) for _ in range(10)]
[3, 4, 'abc', 'abc', 'abc', 'abc', 'abc', 'abc', 'abc', 'abc']

答案 1 :(得分:1)

为什么你不能使用像这样简单的东西?

client.exec_command("/usr/bin/echo q | /usr/bin/htop | aha --black --line-fix >> htop.html")

修改

好评。你对这样的事情有什么看法?

my_list.pop(n) if n < len(my_list) else "default"

该功能与OP的类继承方法相同。然而,为了便于阅读和将来的维护,我通常更喜欢简单的功能而不是创建平凡的类。