Python嵌套列表和迭代

8
我刚开始学习Python,我进行了搜索但是没有找到我需要的答案。如果这个问题已经被问过,或者我由于不知道我想要实现的东西的名称而无法找到它,那么我提前向您道歉。如果您有任何文档建议,我会很愿意阅读。
我有一个列表的列表,例如 => [int,'str']
t = [[0234, 'str_0'],
     [1267, 'str_1'],
     [2445, 'str_2']]

我希望找出一个名为“list t”的列表中,某个列表的索引(index)为1的位置是否存在 a str。我可以使用包含2个for或while循环的函数来实现此操作,但我想要的是使用单次迭代尽可能快地完成此操作。我想了解最短的函数。
对于输入字符串'str_3',我想获得int(2)(具有str_3的列表的索引,其自己的第一个索引位置)对于'str_1',我想获得int(0)
对于str_1234,由于它不在list t中的任何列表中,因此我想获得False 作为新手,我通常会这样做:
for n in range(len(t)):
    if t[n][1] == 'str_1'
        return n
    return False

我所追求的是,如果可能的话,在一行代码中更好、更短地实现这一点,或者简单地学习是否有更聪明、更好或更符合Python风格的方法,您中任何一位肯定更有经验的人都可以推荐。
谢谢。

1
如果您的示例数据是有效的Python代码,那么它们就更有用了,可以用来测试答案。 - Marcin
4个回答

11
[n for n, (i, s) in enumerate(t) if s == 'str_3']

解释:

>>> t = [[100, 'str_1'], [200, 'str_2'], [300, 'str_3']]

# Use enumerate to get each list item along with its index.
>>> list(enumerate(t))
[(0, [100, 'str_1']), (1, [200, 'str_2']), (2, [300, 'str_3'])]

# Use list comprehension syntax to iterate over the enumeration.
>>> [n for n, (i, s) in enumerate(t)]
[0, 1, 2]

# An if condition can be added right inside the list comprehension.
>>> [n for n, (i, s) in enumerate(t) if s == 'str_3']
[2]
>>> [n for n, (i, s) in enumerate(t) if s == 'str_1234']
[]

这将返回所有匹配的索引,因为可能有多个。

如果您知道只会有一个索引,我建议使用字典而不是嵌套列表?使用字典,您可以使用非常直观的语法在常量时间内查找元素,而不必进行迭代。

>>> t = {'str_1': 100, 'str_2': 200, 'str_3': 300}

>>> 'str_3' in t
True
>>> t['str_3']
300

>>> 'str_1234' in t
False
>>> t['str_1234']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'str_1234'

非常感谢您,Kugelman先生。我从您的帖子中学到了很多东西。我考虑使用字典,但我正在尝试创建站点地图的表示形式。每个节点必须具有int_16(id),str_32(name)和int_8(type)元素。如果一个节点(网站的部分)有子节点,我想用另一个列表或元组替换类型。我需要这个来与将url匹配到站点地图的函数一起使用,以便返回404或从数据库中获取。在这种情况下,您有什么建议?非常感谢。 - Phil
@Phil 在评论中回应这个问题有些不太够,因为它很复杂!使用某种嵌套结构确实是一个不错的选择。或许你可以再发一个新问题。 - John Kugelman

7

使用生成器表达式和any函数

any(el[1] == 'str_1' for el in t)

这个代码循环遍历了变量t中的每一个元素el,然后测试其中第二个值是否等于str_1,就像你的循环一样。
但是它只执行到找到第一个满足条件的元素时就停止,返回True。如果没有找到这样的元素,它会返回False。
如果你不仅想测试str_1是否存在,而且还想知道它在哪里,最有效的方法是使用像你所示的循环。你可以使用enumerate函数为你提供序列中的索引和值。
for n, el in enumerate(t):
    if el[1] == 'str_1'
        return n
    return False

1
我可能会使用next代替循环:next((n for n, el in enumerate(t) if el[1] == 'str_1'), False)或类似的东西。【未经测试,但某些变体应该可以工作。】但是循环同样清晰 -- 甚至更加清晰 -- 所以这并不重要。 - DSM
谢谢Pieters先生。我也从您的帖子中学到了很多。不幸的是,任何一个函数都不能独自完成这个技巧,但是您的第二个函数非常棒。 - Phil
@DSM:是的,这感觉就像是为了使用生成器表达式而使用生成器表达式。 - Martijn Pieters

3

您提供的for循环示例并不是Python中通常使用for循环的方式,因为在Python中,for循环不是一个数值结构(像大多数语言中一样),而是一种将循环体应用于给定集合的每个元素产生的迭代的方法。

找到索引的正常方式(使用循环)是:

for (n, (integer, string)) in enumerate(t):
    if 'str1' == string:
        return n

注意,在这种情况下,使用序列赋值将每个两项列表的元素分配给单独的变量。在Python shell中尝试使用enumerate函数以了解其功能。
如果您的列表已排序,您可能希望使用bisecthttp://docs.python.org/library/bisect.html 如果您只想查找是否有符合条件的一对,并且不关心索引,请使用any,它在找到匹配项后停止迭代:
any(string == 'str_1' for integer, string in t)
any 中的表达式是一个生成器表达式,它为 t 中的每个元素产生 TrueFalse
最后,请考虑是否真的使用这种数据结构最合适。

你好Marcin。感谢您提供这个出色的答案。我想创建一个表示复杂站点地图的数据结构。我考虑使用具有3个值的列表(或元组):id、名称和类型。如果节点有子节点,则将type_of替换为另一个列表(或元组)。我正在尝试创建一个函数,将给定的URL与表示站点地图的数据结构匹配。首先,我将url / a / b / c转换为列表,并尝试相应地将每个元素与数据结构匹配。您认为最好的方法是什么? - Phil
@Phil 我认为最好的方法是要么使用现有的URL路由系统,要么看看它们是如何实现的。但是,如果你正在使用树结构,请考虑制作一个明确的树形结构,或者如果符合您的需求,可以考虑使用嵌套字典。 - Marcin

1

首先,让我们创建一个与您所描述的类似的实际数据结构:

>>> LoL=[[i,'str_{}'.format(i)] for i in list(range(10))+list(range(10,0,-1))]
# this is Py 3, so that is why I need 'list' around range
>>> LoL
[[0, 'str_0'], [1, 'str_1'], [2, 'str_2'], [3, 'str_3'], [4, 'str_4'], [5, 'str_5'], 
 [6, 'str_6'], [7, 'str_7'], [8, 'str_8'], [9, 'str_9'], [10, 'str_10'], [9, 'str_9'], 
 [8, 'str_8'], [7, 'str_7'], [6, 'str_6'], [5, 'str_5'], [4, 'str_4'], [3, 'str_3'], 
 [2, 'str_2'], [1, 'str_1']]

现在为每个与“str_5”相等的元素创建一个元组列表。
>>> [(i,li,ls) for (i,(li,ls)) in enumerate(LoL) if ls == 'str_5']
[(5, 5, 'str_5'), (15, 5, 'str_5')]

现在,使用一个不存在的字符串进行测试:

>>> [(i,li,ls) for (i,(li,ls)) in enumerate(LoL) if ls == 'str_123']
[]

这样就很容易测试存在性、计算出现次数并提取所需的项目:

>>> for t in [(i,li,ls) for (i,(li,ls)) in enumerate(LoL) if ls == target]:
...    print('index: {}, int: {}, str: "{}"'.format(*t))
... 
index: 7, int: 7, str: "str_7"
index: 13, int: 7, str: "str_7"

正如其他人所说,您可以重新考虑您的数据结构。您是否将int_x作为索引保留?不要这样做。使用字符串列表和枚举。如果只是str_x和int_x之间的映射,请使用字典。


谢谢你,红萝卜顶部!非常出色的解释。 - Phil

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