为什么没有列表有像字典一样安全的"获取"方法?

CSZ*_*CSZ 234 python exception list indexoutofboundsexception

为什么列表没有像字典那样安全的"获取"方法?

>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'

>>> l = [1]
>>> l[10]
IndexError: list index out of range
Run Code Online (Sandbox Code Playgroud)

Nic*_*tin 97

最终它可能没有一个安全的.get方法,因为a dict是一个关联集合(值与名称相关联),检查一个键是否存在(并返回其值)而不抛出异常是低效的,而它是超级琐碎的避免访问列表元素的异常(因为len方法非常快).该.get方法允许您查询与名称关联的值,而不是直接访问字典中的第37项(这更像是您对列表的要求).

当然,您可以自己轻松实现:

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default
Run Code Online (Sandbox Code Playgroud)

你甚至可以将它monkeypatch到__builtins__.list构造函数中__main__,但这是一个不太普遍的变化,因为大多数代码都没有使用它.如果你只想将它用于你自己的代码创建的列表,你可以简单地子类化list并添加get方法.

  • Python不允许monkeypatching内置类型,如`list` (20认同)
  • 我认为这根本不是效率 - 检查字典中是否存在键和/或返回项目是否为"O(1)".它在原始条件下不会像检查`len`那么快,但从复杂的角度看它们都是'O(1)`.正确的答案是典型的用法/语义... (10认同)
  • 我认为人们在这里没有抓住重点。讨论不应该是关于效率的。请停止过早的优化。如果你的程序太慢,你要么在滥用`.get()`,或者你的代码(或环境)的其他地方有问题。使用这种方法的重点是代码可读性。“vanilla”技术在每个需要完成的地方都需要四行代码。`.get()` 技术只需要一个,并且可以很容易地与后续方法调用链接起来(例如,`my_list.get(2, '').uppercase()`)。 (10认同)
  • @CSZ:`.get`解决了列表中没有的问题 - 在获取可能不存在的数据时避免异常的有效方法.知道有效的列表索引是多么微不足道且非常有效,但是对于字典中的键值没有特别好的方法. (7认同)
  • @Mark:并非所有O(1)都是平等的.此外,`dict`只是最好的情况O(1),并非所有情况. (3认同)

Jak*_*ake 58

如果你想要第一个元素,就可以了 my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'
Run Code Online (Sandbox Code Playgroud)

我知道这不是你要求的,但它可能对其他人有所帮助.

  • 比函数式编程更少pythonic (3认同)
  • `next(iter(my_list[index:index+1]), 'fail')` 允许任何索引,而不仅仅是 0。或者更少的 FP,但可以说更 Pythonic,并且几乎肯定更具可读性: `my_list[index] if index < len(my_list) else '失败'`。 (2认同)

YOU*_*YOU 40

而不是使用.get,使用这样的列表应该没问题.只是用法差异.

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'
Run Code Online (Sandbox Code Playgroud)

  • 如果我们尝试使用-1获取最新元素,则会失败. (14认同)
  • @TylerCrompton:python中没有循环链接列表.如果你自己编写了一个,你就可以自由地实现一个`.get`方法(除了我不确定你如何解释索引在这种情况下的含义,或者为什么它会失败). (3认同)
  • 处理越界负索引的另一种方法是“lst[i] if -len(lst) &lt;= i &lt; len(l) else 'fail'” (2认同)

Kei*_*ith 39

可能是因为它对列表语义没有多大意义.但是,您可以通过子类化轻松创建自己的.

class safelist(list):
    def get(self, index, default=None):
        try:
            return self.__getitem__(index)
        except IndexError:
            return default

def _test():
    l = safelist(range(10))
    print l.get(20, "oops")

if __name__ == "__main__":
    _test()
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,这是回答OP的最蟒蛇.请注意,您还可以提取子列表,这是Python中的安全操作.给定mylist = [1,2,3],您可以尝试使用mylist [8:9]提取第9个元素,而不会触发异常.然后,您可以测试列表是否为空,如果它不为空,则从返回的列表中提取单个元素. (4认同)
  • 仅仅因为你需要一个很好的“get”方法而子类化你自己的列表并没有什么Python风格。可读性很重要。每增加一个不必要的类,可读性都会受到影响。只需使用“try / except”方法,无需创建子类。 (4认同)
  • @Jeyekomon 通过子类化来减少样板代码是完全 Pythonic 的。 (2认同)
  • 为什么不“返回自我[索引]”? (2认同)

Vse*_*aga 16

试试这个:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'
Run Code Online (Sandbox Code Playgroud)


小智 12

您可以做的最好的事情是将列表转换为dict,然后使用get方法访问它:

>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')
Run Code Online (Sandbox Code Playgroud)

  • 一个合理的解决方法,但几乎不是"你能做的最好的事情". (18认同)
  • 但效率很低.注意:可以使用`dict(enumerate(my_list))而不是`zip range len`. (3认同)
  • 这不是最好的事情,这是你能做的最糟糕的事情. (3认同)
  • 如果你考虑性能,这是最糟糕的事情......如果你关心性能,你不会用像python这样的解释语言编码.我发现这个解决方案使用字典相当优雅,强大和pythonic.无论如何,早期的优化是邪恶的,所以让我们有一个词典,稍后再看它是一个瓶颈. (3认同)

qrä*_*bnö 12

jose.angel.jimenez


对于"oneliner"粉丝......


如果您想要列表的第一个元素,或者如果您想要列表为空的默认值,请尝试:

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)
Run Code Online (Sandbox Code Playgroud)

回报 a

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)
Run Code Online (Sandbox Code Playgroud)

回报 default


其他元素的例子......

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']
Run Code Online (Sandbox Code Playgroud)

默认回退...

liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0])  # returns a
print((liste[1:2] or ('default',))[0])  # returns b
print((liste[2:3] or ('default',))[0])  # returns c
Run Code Online (Sandbox Code Playgroud)

经过测试 Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)


rad*_*tek 6

因此,我对此进行了更多研究,结果发现没有任何针对此的东西。当我找到list.index(value)时,我感到很兴奋,它返回指定项目的索引,但是没有什么可用于获取特定索引处的值的。因此,如果您不想使用safe_list_get解决方案,我认为这是相当不错的。以下是一些liner if语句,这些语句可以根据情况为您完成工作:

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) == 4 else 'No'
>>> el
'No'
Run Code Online (Sandbox Code Playgroud)

您也可以使用None代替'No',这更有意义。

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None
Run Code Online (Sandbox Code Playgroud)

另外,如果您只想获取列表中的第一项或最后一项,则可以使用

end_el = x[-1] if x else None
Run Code Online (Sandbox Code Playgroud)

您也可以将它们变成函数,但我仍然喜欢IndexError异常解决方案。我尝试了该safe_list_get解决方案的简化版本,并使其变得更简单(没有默认设置):

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None
Run Code Online (Sandbox Code Playgroud)

还没有进行基准测试以了解最快的方法。


App*_*eue 5

字典是用来查找的。询问条目是否存在是有意义的。列表通常是迭代的。询问 L[10] 是否存在并不常见,而是询问 L 的长度是否为 11。