在Python中获取超出索引范围的默认值

132
a = ['123', '2', 4]
b = a[4] or 'sss'
print b

当列表索引超出范围时,我希望得到一个默认值(这里是'sss')。

我该怎么做?

13个回答

126

按照Python的精神,“宁愿请求原谅,也不要征得许可”,这里有一种方式:

try:
    b = a[4]
except IndexError:
    b = 'sss'

69
有没有简单的方法? - zjm1126
11
@zjm:这确实是一个简单的方法。 - Eli Bendersky
8
需要将其拆分为3个回答吗? - kennytm
14
@KennyTM说:“它们是三个不同的选项。我认为这样最佳答案可以被投票置顶,而糟糕的答案会消失得无影无踪。这不是为了引起声望争夺,实际上这是被接受的做法:http://meta.stackexchange.com/questions/21761/can-i-answer-a-question-multiple-times。” - Thomas
7
我希望字典中的.get()方法也能用在列表中,这样当你使用get方法时,如果该元素不存在,它将默认返回None。请注意保持原意并尽可能地通俗易懂。 - Sam Stoelinga
显示剩余9条评论

111

遵循“征求许可,而非后悔”的非Python精神,这里提供另一种方式:

b = a[4] if len(a) > 4 else 'sss'

4
用过它,看到可靠性得到确认很好。我倾向于尽可能避免尝试(和捕捉)。 - Thiago Macedo
4
为什么要避免使用 try/catch? - Laizer
Python中优化避免引发异常的唯一技术用例是当正常情况下会导致异常被引发时(例如,如果len(a)很少大于4,则使用if/then更便宜)。如果正常情况是len(a)>4,那么使用上述方法的唯一真正原因是代码更短(风险更差的可读性),但是maep的答案使用切片甚至更短... - cowbert
我记不得来源了,@ThiagoMacedo,但我相信Python鼓励在正常流程中使用try/except。虽然从其他语言转换过来可能会感到不寻常,但没有理由认为异常是某种关键错误,特别是如果你预料到它的发生。你可能有一行代码可能会遭受KeyErrorIndexError等多种异常,通过使用几个异常处理程序,你可以清晰快速地处理所有这些异常。这有时可以避免嵌套的if/then/else语句反复测试DivideByZero和其他类似的边缘情况。 - NeilG
我避免使用try/except的原因是为了保持代码的整洁。在我看来,过多的尝试会使整个算法显得冗长而不必要。如今,我同意标准建议是一个不错的选择,但仍然会使用许多像这样的一行语句。 - Thiago Macedo

60

遵循Python的美丽胜于丑陋精神

使用切片和解包技巧的代码高尔夫方法(不确定这在4年前是否有效,但在Python 2.7 + 3.3中是有效的)

b, = a[4:5] or ['sss']

个人认为比包装函数或try-catch更好,但对于初学者来说有些吓人。

使用切片而不解压:

b = a[4] if a[4:] else 'sss'

或者,如果您需要经常这样做,并且不介意制作一个词典

d = dict(enumerate(a))
b = d.get(4, 'sss')

1
你的第一个例子如果列表超过5个元素就无法工作。而使用a[4:5]也不再好用了... - Robert Siemer
2
我使用不解包的拼接方法,因为这样返回的元素是标量而不是大小为1的列表。 - underscore_d
你的第一个答案为什么有额外的逗号?在Python解释器中运行:b,=a[4:] or 'sss',b=a[4:] or 'sss'是相同的。 - north.mister
1
我喜欢在那里使用字典的方式。谢谢! - amonowy
2
短一点的高尔夫球手版本:b=[*a[4:],'sss'][0] - mabel
显示剩余4条评论

26

另一种方法:

b = (a[4:]+['sss'])[0]

4
好的,我喜欢这种简洁的风格。你能解释一下句法吗? - froggythefrog
4
a[4:] 创建一个元素列表,从(包括)索引4开始。这将导致一个空列表。[1, 2, 3][999:] == [] 这就是切片操作的工作方式。如果a['123', '2', 4, 5, 6],它会返回 [5, 6]。然后他添加了元素 'sss',创建了一个新的列表,在这种情况下是['sss']。最后一步 [0] 只取第一个元素,因此返回 'sss',但如果有这样的元素,它将返回 a[4] - vlad-ardelean
7
一种有趣的方法可以实现这一点。但有点难以阅读。 - Alexander Oh
1
这至少在Python 3.5中是唯一的工作方式。由于某种原因,如果4在a中,则调用a [4],否则返回“默认值”始终返回默认值。但是很有趣的是,切片始终返回列表(即使为空)。谢谢Alex! - David Vartanian
4 in a 询问列表中是否存在值为 4,而不是键! - yo'

18

你可以创建自己的列表类:

class MyList(list):
    def get(self, index, default=None):
        return self[index] if -len(self) <= index < len(self) else default

您可以像这样使用它:
>>> l = MyList(['a', 'b', 'c'])
>>> l.get(1)
'b'
>>> l.get(9, 'no')
'no'

l.get(-12) -> IndexError: list index out of range - Eric Duminil
请使用 return self[index] if -len(self) <= index < len(self) else default 替代。 - Jav

11

如果您想要获取第一个元素,可以使用以下方式:

next(iter([1, 2, 3]), None)
我用这个来“展开”列表,可能是在对它进行过滤后。
next((x for x in [1, 3, 5] if x % 2 == 0), None)
cur.execute("SELECT field FROM table")
next(cur.fetchone(), None)

8
您可以为这些情况定义一个小的辅助函数:
def default(x, e, y):
    try:
        return x()
    except e:
        return y

如果函数x没有引发类型为e的异常,则返回函数的返回值;否则返回值y。用法:

b = default(lambda: a[4], IndexError, 'sss')

编辑:仅捕获指定类型的异常。

欢迎提出改进建议!


4
@Thomas:在我看来,这样做很不雅观,并且全捕获except令人担忧。 - Eli Bendersky
2
邮件列表上有一个关于这样一个结构的讨论:http://mail.python.org/pipermail/python-dev/2009-August/091039.html - Thomas
1
虽然这是老话题,但Python仍然是为数不多的将异常视为好事的语言之一。它经常用于流程控制,从而提高了可读性。 - Etse

6
try:
    b = a[4]
except IndexError:
    b = 'sss'

更清晰的方式(仅适用于使用字典的情况):

b = a.get(4,"sss") # exact same thing as above

这里有另一种你可能会喜欢的方式(仅适用于字典):

b = a.setdefault(4,"sss") # if a[4] exists, returns that, otherwise sets a[4] to "sss" and returns "sss"

2

我完全赞成征求许可(即不喜欢使用try...except方法)。然而,当它被封装在一个方法中时,代码变得更加简洁:

def get_at(array, index, default):
    if index < 0: index += len(array)
    if index < 0: raise IndexError('list index out of range')
    return array[index] if index < len(a) else default

b = get_at(a, 4, 'sss')

2
mylist[-1] 应该只获取最后一个元素,但是你的“请求许可”的 get_at 代码并不像那样行为。这显示了“请求许可”是错误哲学的众多原因之一:你检查的东西可能与系统要做的不匹配。也就是说,你不仅在系统为你完成的工作上系统地尝试复制工作,而且你很容易把那个额外的、重复的工作搞错。“请求宽恕”要好得多。 - Alex Martelli
@Alex:很好的发现,我没有想到。Python强大的列表切片语法使这变得稍微复杂了一些……然而,我不同意你从这个特殊情况推断出“总体上来说,请求原谅是好的”的结论。当然,我有偏见,因为我是静态类型的坚定支持者。但无论如何,这只是错误的结论。更好的结论应该是我的需求分析不足(还有可能没有好的方法以可修改的方式获取内部行为,而要么重复代码或触发错误。这指向了一个糟糕的API)。 - Konrad Rudolph
如果你使用以下代码def get_at(array, index, default): try: return array[index] except IndexError: return default,你应该能够避免错误。 - Esteban Küber
@voyager:我知道这一点。但正如我在第一句话中所说的那样,我反对故意触发异常。我更喜欢显式而非隐式,因为它减少了混淆(从而减少错误)的来源,这意味着要注意我的方法可以做什么和不能做什么。 - Konrad Rudolph

1
如果您正在寻找一种可维护的方法来获取索引运算符的默认值,我发现以下内容很有用:
如果您覆盖 operator 模块中的 operator.getitem 方法以添加一个可选的默认参数,则可以获得与原始方法完全相同的行为,同时保持向后兼容性。
def getitem(iterable, index, default=None):
  import operator
  try:
    return operator.getitem(iterable, index)
  except IndexError:
    return default

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接