Why not 't list没有安全的“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
240562 次浏览

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

当然,你可以很容易地实现自己:

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

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

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

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

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()

不要使用.get,像这样使用列表应该是可以的。只是用法上的不同。

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

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

你可以说:我经常需要在字典上查找.get()。在做了十年的全职程序员之后,我认为我不需要把它列在清单上。:)

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

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

我知道这不是你想要的但也许能帮到别人。

所以我对此做了更多的研究,结果是没有什么特别的东西。当我找到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

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

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

试试这个:

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

积分归jose.angel.jimenez格斯汽车


对于“一线线”;球迷……


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

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)测试

如果你

  1. 想要一个眼线,
  2. 宁愿不使用try /,除非在您不需要的代码路径中
  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)
''

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

my_list.get(index, default)

作为

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

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

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

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

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

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

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