说我有元组列表:
list = [(1,5), (1,7), (2,3)]
Python中有没有办法编写像
这样的东西if (1, *) in list: do things
其中*
表示“我不关心此值”?所以我们检查第一个位置是否有1
元组,第二个位置是否有值。
据我所知,其他语言中有特殊机制,但我不知道这个特定问题的名称。那么在Python中有类似的行为吗?
P.S。:我知道我可以在这里使用列表推导。我只是对这种特殊机制感兴趣。
答案 0 :(得分:71)
答案 1 :(得分:33)
像您一样要求的占位符对象本身不受支持,但您可以自己制作类似的内容:
class Any(object):
def __eq__(self, other):
return True
ANYTHING = Any()
lst = [(1,5), (1,7), (2,3)]
__eq__
方法定义了两个对象如何测试相等性。 (有关详细信息,请参阅https://docs.python.org/3/reference/datamodel.html。)在此,ANYTHING
将始终测试与任何对象相等的正数。 (除非该对象也以一种返回False的方式覆盖__eq__
。)
in
运算符仅为列表中的每个元素调用__eq__
。即a in b
执行类似的操作:
for elem in b:
if elem == a:
return True
这意味着,如果您说(1, ANYTHING) in lst
,Python会首先将(1, ANYTHING)
与lst
中的第一个元素进行比较。 (反过来,元组定义__eq__
如果其所有元素' __eq__
都返回True,则返回True。即(x, y) == (a, b)
相当于x==a and y==b
或x.__eq__(a) and y.__eq__(b)
}。)
因此,(1, ANYTHING) in lst
将返回True,而(3, ANYTHING) in lst
将返回False。
另请注意,我重命名了您的列表lst
而不是list
,以防止与内置的list
进行名称冲突。
答案 2 :(得分:10)
并非下面提供的所有解决方案方法都必须有效。我的目标是展示我能想到的每种可能的解决方案 - 在我的回答结束时我提供"基准"结果显示为什么或为什么不使用某种方法而不是另一种方法。我相信这是一种很好的学习方式,我会在答案中无耻地鼓励这样的学习。
set
s >>> a_list = [(1,5), (1,7), (2,3)]
>>>
>>> set([l[0] for l in a_list])
{1, 2}
>>>
>>> 1 in set([l[0] for l in a_list])
True
map()
和匿名函数>>> a_list = [(1,5), (1,7), (2,3)]
>>>
>>> map(lambda x: x[0] == 1, a_list)
[True, True, False]
>>>
>>> True in set(map(lambda x: x[0] == 1, a_list))
True
filter
和匿名函数>>> a_list = [(1,5), (1,7), (2,3)]
>>>
>>> filter(lambda x: x[0] == 1, a_list)
[(1,5), (1,7)]
>>>
>>> len(filter(lambda x: x[0] == 1, a_list)) > 0 # non-empty list
True
from pprint import pprint
from random import randint
from timeit import timeit
N_ITEMS = 1000
N_SIM = 1 * (10 ** 5) # 100K = 100000
a_list = [(randint(0, 100), randint(0, 100)) for _ in range(N_ITEMS)]
set_membership_list_comprehension_time = timeit(
"1 in set([l[0] for l in a_list])",
number = N_SIM,
setup="from __main__ import a_list"
)
bool_membership_map_time = timeit(
"True in set(map(lambda x: x[0] == 1, a_list))",
number = N_SIM,
setup="from __main__ import a_list"
)
nonzero_length_filter_time = timeit(
"len(filter(lambda x: x[0] == 1, a_list)) > 0",
number = N_SIM,
setup="from __main__ import a_list"
)
any_list_comprehension_time = timeit(
"any(t[0] == 1 for t in a_list)",
number = N_SIM,
setup="from __main__ import a_list"
)
results = {
"any(t[0] == 1 for t in a_list)": any_list_comprehension_time,
"len(filter(lambda x: x[0] == 1, a_list)) > 0": nonzero_length_filter_time,
"True in set(map(lambda x: x[0] == 1, a_list))": bool_membership_map_time,
"1 in set([l[0] for l in a_list])": set_membership_list_comprehension_time
}
pprint(
sorted(results.items(), key = lambda x: x[1])
)
[('any(t[0] == 1 for t in a_list)', 2.6685791015625), # winner - Martijn
('1 in set([l[0] for l in a_list])', 4.85234808921814),
('len(filter(lambda x: x[0] == 1, a_list)) > 0', 7.11224889755249),
('True in set(map(lambda x: x[0] == 1, a_list))', 10.343087911605835)]
现在谁笑到最后? ...... Martijn(至少我试过)
故事的道德:不要花费超过10分钟"证明"当另一个用户的答案是事实上正确的答案
时,你的劣质解决方案对于小型测试数据更快更有效答案 3 :(得分:5)
这可以使用list comprehension在Python中完成。 例如:
a= [(1, 2), (3, 4), (4, 5), (1, 4)]
[i for i in a if i[0] == 1]
会给你:
[(1, 2), (1, 4)]
答案 4 :(得分:3)
索引是最简单的,但是如果你想使用类似于你的例子的语法,你想把第一个值分配给一个变量而忽略其余的你可以使用python3' s extended iterable unpacking。
In [3]: [a for a,*_ in l]
Out[3]: [1, 1, 2]
或者使用任何逻辑:
In [4]: l = [(1,5), (1,7), (2,3)]
In [5]: any(a == 1 for a,*_ in l)
Out[5]: True
或者在没有函数调用的情况下模仿任何:
In [23]: l = [(1,5), (1,7), (2,3)]
In [24]: g = (a for a,*_ in l)
In [25]: 1 in g
Out[25]: True
In [26]: list(g)
Out[26]: [1, 2]
答案 5 :(得分:2)
元组中元素的数量也可以处理。
>>> import operator
>>> mylist = [(1,2), (1,5), (4,5,8)]
>>> any(i==1 for i in map(operator.itemgetter(0), mylist))
True
答案 6 :(得分:0)
听起来你真的想要filter()
,而不是any()
:
tuple_list = [(1,5), (1,7), (2,3)]
for pair in filter(lambda pair: (pair[0] == 1), tuple_list):
print "Second value {pair[1]} found from {pair}".format(pair=pair)
...
Second value 5 found from (1, 5)
Second value 7 found from (1, 7)
filter()方法非常棒,因为您可以直接向它提供函数。这允许您指定要过滤的某个键,等等。为了进一步简化它,使用lambda表达式将整个事物变成一个单行。