345

为什么列表没有像字典一样的安全“get”方法?

>>> 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
4

13 回答 13

142

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

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

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default

您甚至可以将它猴子修补到 中的__builtins__.list构造函数上__main__,但这将是一个不太普遍的更改,因为大多数代码不使用它。如果您只想将它​​与您自己的代码创建的列表一起使用,您可以简单地子类list化并添加该get方法。

于 2011-02-26T07:23:05.220 回答
105

如果你想要第一个元素,这很有效,比如my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'

我知道这不完全符合您的要求,但它可能对其他人有所帮助。

于 2014-04-11T04:56:54.153 回答
64

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

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()
于 2011-02-26T07:40:37.473 回答
47

而不是使用.get,使用这样的列表应该是可以的。只是用法上的区别。

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'
于 2011-02-26T07:46:29.963 回答
22

感谢jose.angel.jimenezGus Bus


对于“oneliner”粉丝......</p>


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

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)

返回a

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)

返回default


其他元素的示例……</p>

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']
print(liste[3:4])  # returns []

使用默认后备...</p>

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
print((liste[3:4] or ('default',))[0])  # returns default

可能更短:

liste = ['a', 'b', 'c']
value, = liste[:1] or ('default',)
print(value)  # returns a

看起来您需要在等号、等号和后括号之前使用逗号。


更一般的:

liste = ['a', 'b', 'c']
f = lambda l, x, d: l[x:x+1] and l[x] or d
print(f(liste, 0, 'default'))  # returns a
print(f(liste, 1, 'default'))  # returns b
print(f(liste, 2, 'default'))  # returns c
print(f(liste, 3, 'default'))  # returns default

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

于 2016-12-29T21:17:31.780 回答
19

试试这个:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'
于 2014-11-10T17:50:10.677 回答
16

您可以做的合理的事情是将列表转换为 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')
于 2014-09-19T17:17:17.260 回答
8

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

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'

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

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None

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

end_el = x[-1] if x else None

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

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None

还没有基准测试看看什么是最快的。

于 2014-06-20T16:16:34.990 回答
4

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

于 2011-02-26T07:34:15.200 回答
2

如果你

  1. 想要一个班轮,
  2. 不喜欢尝试 / 除非在您不需要的快乐代码路径中,并且
  3. 希望默认值是可选的,

你可以使用这个:

list_get = lambda l, x, d=None: d if not l[x:x+1] else l[x]

用法如下:

>>> list_get(['foo'], 4) == None
True
>>> list_get(['hootenanny'], 4, 'ho down!')
'ho down!'
>>> list_get([''], 0)
''
于 2020-07-29T03:18:18.097 回答
1

这不是一个非常通用的解决方案,但我有一个案例,我期望一个长度为 3 到 5 的列表(带有保护if),并且我将值分解为命名变量。我为此找到了一种简单而简洁的方法:

foo = (argv + [None, None])[3]
bar = (argv + [None, None])[4]

现在foobar是列表中的第 4 个和第 5 个值,或者None如果没有那么多值。

于 2021-07-23T14:59:49.530 回答
0

对于小的索引值,您可以实现

my_list.get(index, default)

作为

(my_list + [default] * (index + 1))[index]

如果您事先知道索引是什么,那么这可以简化,例如,如果您知道它是 1,那么您可以这样做

(my_list + [default, default])[index]

因为列表是前向打包的,所以我们需要担心的唯一失败情况是超出列表的末尾。这种方法用足够的默认值填充列表的末尾以保证索引被覆盖。

于 2020-09-04T09:37:14.317 回答
-2

你的用例基本上只与做固定长度的数组和矩阵有关,这样你就知道它们有多长了。在这种情况下,您通常还会在使用 None 或 0 手动填充它们之前创建它们,这样实际上您将使用的任何索引都已经存在。

你可以这样说:我经常需要字典上的 .get() 。作为一名全职程序员十年后,我认为我从来不需要它在列表中。:)

于 2011-02-26T08:30:33.800 回答