Python итерации над словарями

У меня есть два словаря. Я повторяю его один внутри другого. Я хочу выбрать следующий элемент каждый раз, когда он повторяется во внутреннем цикле, а не с самого начала.

dict1={'a':1 , 'b':2 , 'c':3}
dict2={'x':10, 'y':20, 'z':30}

for key,value in dict1:
   #do something
    for k,v in dict2:
          #do something

Когда key = 'a', он выполняет итерацию по внутреннему циклу и выполняет какое-то действие в соответствии с кодом в этом цикле. Предположим, что это действие, которое оно выбрало, было "x" в dict2. Теперь, когда мне нужно итерации с ключом = 'b', я хочу, чтобы итерация для внутреннего цикла начиналась с 'y', потому что 'x' уже был выбран.

Ответы

Ответ 1

Во-первых, for key,value in dict1 не делает то, что вы хотите - он просто выполняет итерации по клавишам. Если вы хотите перебрать пару ключ-значение, вам нужно for key, value in dict1.items().

Между тем, похоже, что вы запрашиваете, это итерация блокировки, а не вложенная итерация? Если это так, вам нужна функция zip:

for (k1, v1), (k2, v2) in zip(dict1.items(), dict2.items()):
    # do something

Но обратите внимание, что упорядочение, которое это дает вам, фактически будет произвольным, потому что упорядочение dicts по своей сути произвольно. Итак, если вы довольны ('a', 'x'), затем ('b', 'y') и т.д., А также довольны ('a', 'y'), тогда ('b', 'x'), тогда zip - это то, что вы хотите. Если нет, это не так.

(Если вы ожидали, что ваш dict имеет некоторый собственный порядок, например порядок вставки или отсортированный порядок, или что-нибудь еще, вам придется использовать другой класс. Например, collections.OrderedDict действует как словарь, но сохраняет порядок вставки.)


Если вам нужно что-то более сложное, где вы перемещаете итерацию по dict2 программно так, как вы не можете выразить в однострочном пространстве, вы всегда можете использовать протокол итерации вручную:

iter2 = iter(dict2.items())
for k1, v1 in dict1.items():
    k2, v2 = next(iter2)
    # do something
    # maybe do another k2, v2 = next(iter2)
    # maybe restart iter2 = iter(dict2.items())
    # and so on

Как объясняют документы, когда next(iter2) подходит к концу, он поднимет StopIteration. Что, если вы этого не хотите? Может быть, вы хотите, как вы сказали в комментарии, начать заново? Вы можете решить это, поймав StopIteration, или используя форму с двумя аргументами next и проверку для часового. Например:

try:
    k2, v2 = next(iter2)
except StopIteration:
    iter2 = iter(dict2.items())
    k2, v2 = next(iter2)

Или вы даже можете написать круговую итерационную оболочку:

def circular_iterate(iterable_factory):
    while True:
        yield from iterable_factory():

В Python 3.2 или более ранней версии вы должны заменить yield from на внутренний цикл:

def circular_iterate(iterable_factory):
    while True:
        for value in iterable_factory():
            yield value

И теперь:

iter2 = circular_iterate(dict2.items) # note, not dict2.items()

Если вы пытаетесь отслеживать больше, чем просто "текущую позицию", вам, вероятно, потребуется дополнительная структура данных. Например, если вы хотите пропустить все обнаруженные до сих пор клавиши, используйте set для ключей, которые были просмотрены до сих пор, или set ключей, которые не были видны:

seen_items_2 = set()
for k1, v1 in dict1.items():
    for k2, v2 in dict2.items():
        if k2 in seen_items_2:
            continue
        seen_items_2.add(k2)
        # do something