我的代码看起来像这样:
thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)
好的,这样简化但你明白了。现在thing
可能实际上不在列表中,在这种情况下我想将-1传递为thing_index
。在其他语言中,如果找不到该元素,则这是您期望index()
返回的内容。实际上它会抛出一个ValueError
。
我可以这样做:
try:
thing_index = thing_list.index(thing)
except ValueError:
thing_index = -1
otherfunction(thing_list, thing_index)
但这感觉很脏,而且我不知道ValueError
是否可以因为其他原因而被提出。我想出了基于生成器函数的以下解决方案,但它看起来有点复杂:
thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]
有没有更简洁的方法来实现同样的目标?我们假设列表没有排序。
答案 0 :(得分:56)
使用try-except子句没有“脏”。这是pythonic的方式。仅ValueError
方法会引发.index
,因为它是您在那里唯一的代码!
回答评论:
在Python中,easier to ask forgiveness than to get permission哲学已经建立,否 index
不会因任何其他问题引发此类错误。不是我能想到的。
答案 1 :(得分:41)
thing_index = thing_list.index(elem) if elem in thing_list else -1
一行。简单。没有例外。
答案 2 :(得分:15)
dict
type有一个get
function,如果字典中不存在该键,则get
的第二个参数是它应该返回的值。类似地,有setdefault
,如果密钥存在,则返回dict
中的值,否则根据默认参数设置值,然后返回默认参数。
您可以将list
类型扩展为getindexdefault
方法。
class SuperDuperList(list):
def getindexdefault(self, elem, default):
try:
thing_index = self.index(elem)
return thing_index
except ValueError:
return default
然后可以使用:
mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )
答案 3 :(得分:5)
使用ValueError
的代码没有任何问题。如果你想避免例外,这里还有另一个单行:
thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)
答案 4 :(得分:3)
这个问题是语言哲学之一。例如,在Java中,一直存在一种传统,即异常应该仅用于发生错误的“异常情况”,而不是flow control。起初这是出于性能原因,因为Java异常很慢,但现在这已成为公认的风格。
相比之下,Python总是使用异常来指示正常的程序流,就像我们在这里讨论的那样引发ValueError
。在Python风格中没有任何“脏”的东西,还有更多来自它的地方。一个更常见的例子是StopIteration
exception,它由迭代器的next()
方法引发,表示没有其他值。
答案 5 :(得分:1)
这个怎么样:
otherfunction(thing_collection, thing)
不是像函数接口中的列表索引那样公开依赖于实现的东西,而是传递集合和东西,让其他函数处理“测试成员资格”问题。如果其他函数被编写为与集合类型无关,那么它可能会以:
开头if thing in thing_collection:
... proceed with operation on thing
如果thing_collection是list,tuple,set或dict,它将起作用。
这可能比:
更清晰if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:
这是你在其他功能中已有的代码。
答案 6 :(得分:1)
怎么这样:
temp_inx = (L + [x]).index(x)
inx = temp_inx if temp_inx < len(L) else -1
答案 7 :(得分:1)
已经有一段时间了,但它是 stdlib 的核心部分,并且有几十种潜在的方法,所以我认为为不同的建议制定一些基准很有用,并包括迄今为止最快的 numpy 方法。
>import random
from timeit import timeit
import numpy as np
l = [random.random() for i in range(10**4)]
l[10**4 - 100] = 5
# method 1
def fun1(l:list, x:int, e = -1) -> int:
return [[i for i,elem in enumerate(l) if elem == x] or [e]][0]
# method 2
def fun2(l:list, x:int, e = -1) -> int:
for i,elem in enumerate(l):
if elem == x:
return i
else:
return e
# method 3
def fun3(l:list, x:int, e = -1) -> int:
try:
idx = l.index(x)
except ValueError:
idx = e
return idx
# method 4
def fun4(l:list, x:int, e = -1) -> int:
return l.index(x) if x in l else e
l2 = np.array(l)
# method 5
def fun5(l:list or np.ndarray, x:int, e = -1) -> int:
res = np.where(np.equal(l, x))
if res[0].any():
return res[0][0]
else:
return e
if __name__ == "__main__":
print("Method 1:")
print(timeit(stmt = "fun1(l, 5)", number = 1000, globals = globals()))
print("")
print("Method 2:")
print(timeit(stmt = "fun2(l, 5)", number = 1000, globals = globals()))
print("")
print("Method 3:")
print(timeit(stmt = "fun3(l, 5)", number = 1000, globals = globals()))
print("")
print("Method 4:")
print(timeit(stmt = "fun4(l, 5)", number = 1000, globals = globals()))
print("")
print("Method 5, numpy given list:")
print(timeit(stmt = "fun5(l, 5)", number = 1000, globals = globals()))
print("")
print("Method 6, numpy given np.ndarray:")
print(timeit(stmt = "fun5(l2, 5)", number = 1000, globals = globals()))
print("")
当作为主程序运行时,这会在我的机器上产生以下打印输出,以秒为单位指示完成每个函数 1000 次试验的时间:
方法一: 0.7502102799990098
方法二: 0.7291318440002215
方法三: 0.24142152300009911
方法四: 0.5253471979995084
方法五,numpy给定列表: 0.5045417560013448
方法六,numpy给定np.ndarray: 0.011147511999297421
当然,该问题专门询问列表,因此最好的解决方案是使用 try-except 方法,但是通过使用 numpy 数据结构和运算符而不是python 数据结构很重要,如果在许多对性能至关重要的数据数组上构建某些东西,那么作者应该尝试始终使用 numpy 来利用超快的 C 绑定。 (CPython 解释器,其他解释器性能可能有所不同)
顺便说一句,方法 5 比方法 6 慢得多的原因是因为 numpy 首先必须将给定的列表转换为它自己的 numpy 数组,所以给它一个列表不会破坏它只是没有充分利用速度可能。
答案 8 :(得分:0)
我对列表中的“.index()”方法有同样的问题。我没有问题它会引发异常,但我强烈不同意这是一个非描述性的ValueError这一事实。我能理解它是否会成为一个IndexError。
我可以看到为什么返回“-1”也是一个问题,因为它是Python中的有效索引。但实际上,我从不期望“.index()”方法返回负数。
这里有一个班轮(好吧,这是一个相当长的行...),只列出一次列表,如果找不到该项,则返回“无”。如果您愿意,重写它以返回-1将是微不足道的。
indexOf = lambda list, thing: \
reduce(lambda acc, (idx, elem): \
idx if (acc is None) and elem == thing else acc, list, None)
使用方法:
>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>
答案 9 :(得分:0)
如果你经常这样做,那么最好把它放在辅助功能中去除:
def index_of(val, in_list):
try:
return in_list.index(val)
except ValueError:
return -1
答案 10 :(得分:0)
那?:
li = [1,2,3,4,5] # create list
li = dict(zip(li,range(len(li)))) # convert List To Dict
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None
li.get(1) # 0
答案 11 :(得分:0)
Python 3.8 的简单对比
TL;DR maybeidx2
通常更快,除了有很多未命中的数组 (n<100)
def maybeidx1(l, v):
return l.index(v) if v in l else None
def maybeidx2(l, v):
try:
return l.index(v)
except ValueError:
return None
a = [*range(100_000)]
# Case 1: index in list
maybeidx1(a, 50_000)
Out[20]: 50000
maybeidx2(a, 50_000)
Out[21]: 50000
# Case 2: index not in list
maybeidx1(a, 100_000) is None
Out[23]: True
maybeidx2(a, 100_000) is None
Out[24]: True
%timeit maybeidx1(a, 50_000)
1.06 ms ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit maybeidx2(a, 50_000)
530 µs ± 8.47 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit maybeidx1(a, 100_000)
1.07 ms ± 21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit maybeidx2(a, 100_000)
1.07 ms ± 16.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
对较大的数组使用 maybeidx2
方法。由于 maybeidx1
对数组进行两次扫描以搜索该值,因此速度更快 - 这仍然是 O(n) 时间,但乘数为 2,因此在实践中较慢。这适用于列表中存在值的情况。当该值不存在时,这些时间将大致相等;他们都必须准确地扫描整个数组一次,然后返回 None
。 try-except
的开销可以忽略不计,即使数组大小为 10 - 除非出现两种情况。然后 try-except
开销很明显。示例:
a = [*range(10)]
%timeit maybeidx1(a, 10)
191 ns ± 2.61 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit maybeidx2(a, 10)
566 ns ± 5.93 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
当 a
的元素超过 100 个时,这种开销变得可以忽略不计(在我的机器上)。
答案 12 :(得分:-2)
我不知道你为什么认为它很脏...因为例外?如果你想要一个oneliner,这里是:
thing_index = thing_list.index(elem) if thing_list.count(elem) else -1
但我建议不要使用它;我认为Ross Rogers解决方案是最好的,使用一个对象来封装你的desiderd行为,不要试图以可读性为代价将语言推到极限。
答案 13 :(得分:-2)
我建议:
if thing in thing_list:
list_index = -1
else:
list_index = thing_list.index(thing)