Say I have list of tuples:
说我有元组列表:
list = [(1,5), (1,7), (2,3)]
Is there a way in Python to write something like
在Python中有没有办法写出类似的东西
if (1, *) in list: do things
where *
means "I don’t care about this value"? So we are checking if there is a tuple with 1
at the first position and with whatever value on the second one.
其中*表示“我不关心这个价值”?所以我们正在检查第一个位置是否有1的元组,第二个位置是否有任何值。
As far as I know there are special mechanisms in other languages, but I just don’t know the name of this particular problem. So is there similar behavior in Python?
据我所知,其他语言中有特殊机制,但我只是不知道这个特定问题的名称。那么在Python中有类似的行为吗?
P.S.: I know that I can use list comprehensions here. I am just interested in this particular mechanism.
P.S。:我知道我可以在这里使用列表推导。我只是对这种特殊机制感兴趣。
7 个解决方案
#1
33
A placeholder object like you're asking for isn't supported natively, but you can make something like that yourself:
像你要求的占位符对象本身不受支持,但你可以自己制作类似的东西:
class Any(object):
def __eq__(self, other):
return True
ANYTHING = Any()
lst = [(1,5), (1,7), (2,3)]
The __eq__
method defines how two objects test for equality. (See https://docs.python.org/3/reference/datamodel.html for details.) Here, ANYTHING
will always test positive for equality with any object. (Unless that object also overrode __eq__
in a way to return False.)
__eq__方法定义了两个对象如何测试相等性。 (有关详细信息,请参阅https://docs.python.org/3/reference/datamodel.html。)此处,ANYTHING将始终测试与任何对象的相等性。 (除非该对象也以某种方式覆盖__eq__以返回False。)
The in
operator merely calls __eq__
for each element in your list. I.e. a in b
does something like:
in运算符仅为列表中的每个元素调用__eq__。即a in b的确如下:
for elem in b:
if elem == a:
return True
This means that, if you say (1, ANYTHING) in lst
, Python will first compare (1, ANYTHING)
to the first element in lst
. (Tuples, in turn, define __eq__
to return True if all its elements' __eq__
return True. I.e. (x, y) == (a, b)
is equivalent to x==a and y==b
, or x.__eq__(a) and y.__eq__(b)
.)
这意味着,如果你在lst中说(1,ANYTHING),Python将首先将(1,ANYTHING)与lst中的第一个元素进行比较。 (反过来,元组定义__eq__,如果其所有元素'__eq__都返回True,则返回True。即(x,y)==(a,b)相当于x == a和y == b,或x .__ eq__ (a)和y .__ eq __(b)。)
Hence, (1, ANYTHING) in lst
will return True, while (3, ANYTHING) in lst
will return False.
因此,lst中的(1,ANYTHING)将返回True,而lst中的(3,ANYTHING)将返回False。
Also, note that I renamed your list lst
instead of list
to prevent name *es with the Python built-in list
.
另请注意,我重命名了列表lst而不是list,以防止与Python内置列表发生名称冲突。
#2
71
You can use the any()
function:
你可以使用any()函数:
if any(t[0] == 1 for t in yourlist):
This efficiently tests and exits early if 1
is found in the first position of a tuple.
如果在元组的第一个位置找到1,则可以有效地测试和退出。
#3
10
Not all of my solution methods provided below will be necessarily efficient. My goal is to demonstrate every possible solution method I can think of - at the end of my answer I provide "benchmark" results to show why or why not you should use one certain method over another. I believe that is a good way of learning, and I will shamelessly encourage such learning in my answers.
并非下面提供的所有解决方案方法都必然有效。我的目标是展示我能想到的每种可能的解决方法 - 在我的答案结束时,我提供“基准”结果,以显示为什么或为什么不应该使用某种方法而不是另一种方法。我相信这是一种很好的学习方式,我会在答案中无耻地鼓励这样的学习。
Subset + hash 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()
, and anonymous functions
>>> 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
and anonymous functions
>>> 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
MICROBENCHMARKS
Conditions
- 1000 items
- 1000件物品
- 100K repetition
- 100K重复
- 0-100 random range
- 0-100随机范围
- Python 2.7.10, IPython 2.3.0
- Python 2.7.10,IPython 2.3.0
Script
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])
)
Results (in seconds)
[('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)]
Who's got the last laugh now? ... Martijn (at least I tried)
谁现在笑到最后? ...... Martijn(至少我试过)
MORAL OF THE STORY: Don't spend more than 10 minutes "proving" your inferior solution is faster and more efficient on a small test data, when another user's answer is the de-facto correct one
故事的道理:当另一个用户的答案是事实上的正确答案时,不要花费超过10分钟“证明”你的劣质解决方案对小型测试数据更快更有效
#4
5
This can be done in Python using list comprehension. ex:
这可以使用list comprehension在Python中完成。例如:
a= [(1, 2), (3, 4), (4, 5), (1, 4)]
[i for i in a if i[0] == 1]
Will give you:
会给你:
[(1, 2), (1, 4)]
#5
3
Indexing is the simplest but if you wanted to use syntax similar to your example where you wanted to assign the first value to a variable and ignore the rest you could use python3's extended iterable unpacking.
索引是最简单的,但是如果你想使用类似于你的例子的语法,你想要将第一个值赋给变量而忽略其余的你可以使用python3的扩展可迭代解包。
In [3]: [a for a,*_ in l]
Out[3]: [1, 1, 2]
Or with the any logic:
或者使用任何逻辑:
In [4]: l = [(1,5), (1,7), (2,3)]
In [5]: any(a == 1 for a,*_ in l)
Out[5]: True
Or mimicking any without the function call:
或者在没有函数调用的情况下模仿任何:
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]
#6
2
number of element in tuple could be handled also.
元组中元素的数量也可以处理。
>>> import operator
>>> mylist = [(1,2), (1,5), (4,5,8)]
>>> any(i==1 for i in map(operator.itemgetter(0), mylist))
True
#7
0
It sounds like you actually want filter()
, not any()
:
听起来你真的想要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)
The filter() method is great because you can provide a function directly to it. This lets you specify a certain key to filter on, etc. To simplify it further, use a lambda expression to make the entire thing into a one-liner.
filter()方法很棒,因为你可以直接为它提供一个函数。这允许您指定要过滤的某个键,等等。为了进一步简化它,使用lambda表达式将整个事物变成一个单行。
#1
33
A placeholder object like you're asking for isn't supported natively, but you can make something like that yourself:
像你要求的占位符对象本身不受支持,但你可以自己制作类似的东西:
class Any(object):
def __eq__(self, other):
return True
ANYTHING = Any()
lst = [(1,5), (1,7), (2,3)]
The __eq__
method defines how two objects test for equality. (See https://docs.python.org/3/reference/datamodel.html for details.) Here, ANYTHING
will always test positive for equality with any object. (Unless that object also overrode __eq__
in a way to return False.)
__eq__方法定义了两个对象如何测试相等性。 (有关详细信息,请参阅https://docs.python.org/3/reference/datamodel.html。)此处,ANYTHING将始终测试与任何对象的相等性。 (除非该对象也以某种方式覆盖__eq__以返回False。)
The in
operator merely calls __eq__
for each element in your list. I.e. a in b
does something like:
in运算符仅为列表中的每个元素调用__eq__。即a in b的确如下:
for elem in b:
if elem == a:
return True
This means that, if you say (1, ANYTHING) in lst
, Python will first compare (1, ANYTHING)
to the first element in lst
. (Tuples, in turn, define __eq__
to return True if all its elements' __eq__
return True. I.e. (x, y) == (a, b)
is equivalent to x==a and y==b
, or x.__eq__(a) and y.__eq__(b)
.)
这意味着,如果你在lst中说(1,ANYTHING),Python将首先将(1,ANYTHING)与lst中的第一个元素进行比较。 (反过来,元组定义__eq__,如果其所有元素'__eq__都返回True,则返回True。即(x,y)==(a,b)相当于x == a和y == b,或x .__ eq__ (a)和y .__ eq __(b)。)
Hence, (1, ANYTHING) in lst
will return True, while (3, ANYTHING) in lst
will return False.
因此,lst中的(1,ANYTHING)将返回True,而lst中的(3,ANYTHING)将返回False。
Also, note that I renamed your list lst
instead of list
to prevent name *es with the Python built-in list
.
另请注意,我重命名了列表lst而不是list,以防止与Python内置列表发生名称冲突。
#2
71
You can use the any()
function:
你可以使用any()函数:
if any(t[0] == 1 for t in yourlist):
This efficiently tests and exits early if 1
is found in the first position of a tuple.
如果在元组的第一个位置找到1,则可以有效地测试和退出。
#3
10
Not all of my solution methods provided below will be necessarily efficient. My goal is to demonstrate every possible solution method I can think of - at the end of my answer I provide "benchmark" results to show why or why not you should use one certain method over another. I believe that is a good way of learning, and I will shamelessly encourage such learning in my answers.
并非下面提供的所有解决方案方法都必然有效。我的目标是展示我能想到的每种可能的解决方法 - 在我的答案结束时,我提供“基准”结果,以显示为什么或为什么不应该使用某种方法而不是另一种方法。我相信这是一种很好的学习方式,我会在答案中无耻地鼓励这样的学习。
Subset + hash 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()
, and anonymous functions
>>> 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
and anonymous functions
>>> 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
MICROBENCHMARKS
Conditions
- 1000 items
- 1000件物品
- 100K repetition
- 100K重复
- 0-100 random range
- 0-100随机范围
- Python 2.7.10, IPython 2.3.0
- Python 2.7.10,IPython 2.3.0
Script
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])
)
Results (in seconds)
[('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)]
Who's got the last laugh now? ... Martijn (at least I tried)
谁现在笑到最后? ...... Martijn(至少我试过)
MORAL OF THE STORY: Don't spend more than 10 minutes "proving" your inferior solution is faster and more efficient on a small test data, when another user's answer is the de-facto correct one
故事的道理:当另一个用户的答案是事实上的正确答案时,不要花费超过10分钟“证明”你的劣质解决方案对小型测试数据更快更有效
#4
5
This can be done in Python using list comprehension. ex:
这可以使用list comprehension在Python中完成。例如:
a= [(1, 2), (3, 4), (4, 5), (1, 4)]
[i for i in a if i[0] == 1]
Will give you:
会给你:
[(1, 2), (1, 4)]
#5
3
Indexing is the simplest but if you wanted to use syntax similar to your example where you wanted to assign the first value to a variable and ignore the rest you could use python3's extended iterable unpacking.
索引是最简单的,但是如果你想使用类似于你的例子的语法,你想要将第一个值赋给变量而忽略其余的你可以使用python3的扩展可迭代解包。
In [3]: [a for a,*_ in l]
Out[3]: [1, 1, 2]
Or with the any logic:
或者使用任何逻辑:
In [4]: l = [(1,5), (1,7), (2,3)]
In [5]: any(a == 1 for a,*_ in l)
Out[5]: True
Or mimicking any without the function call:
或者在没有函数调用的情况下模仿任何:
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]
#6
2
number of element in tuple could be handled also.
元组中元素的数量也可以处理。
>>> import operator
>>> mylist = [(1,2), (1,5), (4,5,8)]
>>> any(i==1 for i in map(operator.itemgetter(0), mylist))
True
#7
0
It sounds like you actually want filter()
, not any()
:
听起来你真的想要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)
The filter() method is great because you can provide a function directly to it. This lets you specify a certain key to filter on, etc. To simplify it further, use a lambda expression to make the entire thing into a one-liner.
filter()方法很棒,因为你可以直接为它提供一个函数。这允许您指定要过滤的某个键,等等。为了进一步简化它,使用lambda表达式将整个事物变成一个单行。