在列表中查找包含未知值的元组结构

sel*_*tec 34 python tuples list

说我有元组列表:

list = [(1,5), (1,7), (2,3)]
Run Code Online (Sandbox Code Playgroud)

有没有办法在Python中编写类似的东西

if (1, *) in list: do things
Run Code Online (Sandbox Code Playgroud)

在哪里*意味着" 我不关心这个价值 "?所以我们检查1第一个位置是否有元组,第二个位置是否有值.

据我所知,其他语言中有特殊机制,但我不知道这个特殊问题的名称.那么Python中有类似的行为吗?

PS:我知道我可以在这里使用列表推导.我对这个特殊机制感兴趣.

Mar*_*ers 71

你可以使用这个any()功能:

if any(t[0] == 1 for t in yourlist):
Run Code Online (Sandbox Code Playgroud)

如果1在元组的第一个位置找到,这将有效地测试并提前退出.

  • @acdr:这是对问题的不同解释.对不起,你发现我的回答没有帮助或启发,但这是解决潜在问题的正确方法. (13认同)
  • 对于它的价值,我对这个答案进行了投票,尽管这是我解决OP发布问题的方法.但是,考虑到线程的标题,我不认为这个答案(或者除了我自己的任何其他当前的答案)实际上说OP想知道什么(即如何获得具有"任何"值的变量). (3认同)
  • @WorldSEnder:替代数据结构完全超出了范围; OP确实在他们的问题中发布了一个列表. (3认同)

acd*_*cdr 33

像你要求的占位符对象本身不受支持,但你可以自己制作类似的东西:

class Any(object):
    def __eq__(self, other):
        return True
ANYTHING = Any()

lst = [(1,5), (1,7), (2,3)]
Run Code Online (Sandbox Code Playgroud)

__eq__方法定义了两个对象如何测试相等性.(有关详细信息,请参阅https://docs.python.org/3/reference/datamodel.html.)此处,ANYTHING始终会测试与任何对象的相等性.(除非该对象也__eq__以返回False的方式覆盖.)

in运营商仅仅要求__eq__在您的列表中的每个元素.即a in b做以下事情:

for elem in b:
    if elem == a:
        return True
Run Code Online (Sandbox Code Playgroud)

这意味着,如果你说(1, ANYTHING) in lst,Python将首先与(1, ANYTHING)第一个元素进行比较lst.(反过来,元组定义__eq__为返回True,如果它的所有元素都__eq__返回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防止与内置Python的名称冲突list.

  • 抢占其他评论:从技术上讲,当涉及重新定义`__eq__`的欺骗时,还应该重新定义`__ne__`,但在这种情况下不需要.另请注意,`ANYTHING == x`将返回True而不管`x`的值,但同样不一定是'x == ANYTHING`. (10认同)
  • 非常小心使用这样的解决方案...给定的解决方案适用于列表,但不适用于字典之类的东西:`d = {(1,1):'A'}; (1,ANYTHING)在d`返回'False`.这是因为在这种情况下,它实际上是计算源的哈希并直接查找.基本上,任何时候对象的查找都是O(1),如果没有显式地将它转换为O(n)查找(例如在d.keys()中的`(1,ANYTHING),它确实会返回`True`. ).只要确保你不要盲目地使用这个...... (10认同)
  • 如果我在评论中看到这样的代码,我会用火来杀死它并坚持按照Martijn的建议完成它. (6认同)
  • @AlokThakur:这里有两种不同的功能,听起来非常相似.确实本机支持检查迭代的任何元素是否为True,但OP听起来并不像他想知道的那样.第二个问题,更有趣的是,有一个对象(OP的帖子中的`*`,我的'ANYTHING`),它是"任何值"的占位符,与"any"提供的功能无关.内置.(也就是说,我编辑了我的答案,以澄清本来不支持的内容.) (2认同)

Edd*_*oso 10

并非下面提供的所有解决方案方法都必须有效.我的目标是展示我能想到的每种可能的解决方法 - 在我的答案结束时,我提供"基准"结果,以显示为什么或为什么不应该使用某种方法而不是另一种方法.我相信这是一种很好的学习方式,我会在答案中无耻地鼓励这种学习.


子集+哈希sets

>>> 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

微基准测试

条件

  • 1000件物品
  • 重复100K
  • 0-100随机范围
  • Python 2.7.10,IPython 2.3.0

脚本

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])
)
Run Code Online (Sandbox Code Playgroud)

结果(以秒为单位)

[('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)]
Run Code Online (Sandbox Code Playgroud)

谁现在笑到最后?...... Martijn(至少我试过)

故事的道理:当一个小的测试数据,另一个用户的答案是事实上正确的答案时,不要花费超过10分钟"证明"你的劣质解决方案更快更有效

  • @MartijnPieters:哈哈哈我很抱歉 - 我实际上试着让我听起来像是尊重你的专业知识和知识,比如你已经知道一切,而且你比老师更像老师.但事后看来,我听起来像一个令人难以置信的混蛋(原谅我的语言)!但不,那是我的坏事,我不打算这样说.我见过你在其他地方帮助别人,我很欣赏你为社区所做的一切.伙计,我现在觉得有点不好.道歉我的好人! (5认同)
  • 感谢这个反应.我们都需要不时[重新学习](http://blog.codinghorror.com/the-ten-commandments-of-egoless-programming/). (4认同)

小智 5

这可以使用list comprehension在Python中完成.例如:

a= [(1, 2), (3, 4), (4, 5), (1, 4)]
[i for i in a if i[0] == 1]
Run Code Online (Sandbox Code Playgroud)

会给你:

[(1, 2), (1, 4)]
Run Code Online (Sandbox Code Playgroud)