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

问题描述 投票:0回答:13

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

>>> 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
python list exception indexoutofboundsexception
13个回答
166
投票

最终它可能没有安全的

.get
方法,因为
dict
是一个关联集合(值与名称关联),在不抛出异常的情况下检查键是否存在(并返回其值)效率很低。异常,而避免访问列表元素的异常是非常简单的(因为
len
方法非常快)。
.get
方法允许您查询与名称关联的值,而不是直接访问字典中的第 37 项(这更像是您对列表的要求)。

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

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

您甚至可以将其猴子补丁到

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


132
投票

如果您想要第一个元素,则此方法有效,例如

my_list.get(0)

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

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


73
投票

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

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

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

if __name__ == "__main__":
    _test()

50
投票

对于列表来说,像这样使用而不是使用 .get 应该没问题。只是用法不同。

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'

28
投票

感谢jose.angel.jimenezGus Bus


对于“oneliner”粉丝……


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

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


其他元素的示例...

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 []

使用默认后备…

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)

进行测试

22
投票

试试这个:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'

22
投票

您可以做的一个合理的事情是将列表转换为字典,然后使用 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')

9
投票

所以我对此做了更多研究,结果发现没有任何具体的内容。当我发现 list.index(value) 时,我很兴奋,它返回指定项目的索引,但没有任何东西可以获取特定索引处的值。 因此,如果您不想使用我认为非常好的 safe_list_get 解决方案。以下是一些 1 行 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

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


5
投票

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


2
投票

如果你

  1. 想要一件内衬,
  2. 不喜欢在你不需要的地方尝试 / except 在你快乐的代码路径中,并且
  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)
''

2
投票

这不是一个非常通用的解决方案,但我有一个情况,我期望长度为 3 到 5 的列表(带有保护

if
),并且我将值分解为命名变量。 我找到了一个简单而简洁的方法:

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

现在

foo
bar
是列表中的第四个和第五个值,或者
None
(如果没有那么多值)。


1
投票

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

my_list.get(index, default)

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

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

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

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


-2
投票

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

你可以这样说:我经常需要字典上的 .get() 。作为一名全职程序员十年之后,我认为我不再需要将它列入清单。 :)

© www.soinside.com 2019 - 2024. All rights reserved.