Несколько циклов " for " в генераторе словарей

на эта документация Python в качестве примера выражения генератора используется следующее:

dict((fn(i+1), code)
    for i, code in enumerate('FGHJKMNQUVXZ')
    for fn in (int, str))

>> {1: 'F', '1': 'F', 2: 'G', '2': 'G', 3: 'H', '3': 'H', 4: 'J',...}

Я не понимаю, как второй for цикл for fn in (int, str), получается значение типа int в строку и добавляет дополнительную запись в словарь.

Я нашел этот вопрос переполнения стека, но я все еще не мог интуитивно понять, как второй for loop работает в этом случае.

5 ответов


в вашем примере вы строите словарь, используя последовательность (здесь предоставленную выражением генератора) кортежей, выраженных следующим литералом

(fn(i+1), code)

в этом кортежном литерале все термины (кроме 1:) обеспечиваются двумя петлями; внешний цикл

… for i, code in enumerate('TROIDSNB') …

предлагает вам i целочисленное значение и code, строка длиной 1 символ-эти значения фиксируются при выполнении внутреннего цикла;
внутренний цикл предоставляет вам значение fn

… for fn in (int, str) …

это может принимать 2 значения, либо fn=int или fn=str.

когда построен первый кортеж,i=0, code='T' и fn=int

(int(0+1), 'T')

когда построен второй кортеж,i и code (представленной внешний loop) не изменились, но fn=str, поэтому новый Кортеж, переданный конструктору словаря, является

(str(0+1), 'T')

в этот момент внутренняя петля достигла его конец ... внешний цикл обновляет значения своих переменных,i=1 и code=R, внутренний цикл сбрасывается, следовательно fn=int и генерируется новый Кортеж

(int(1+1), 'R')

etc etc


это может помочь "развернуть" петли в выражении генератора и записать их как независимые for петли. Для этого вы берете все for (variable) in (iterable) заявления и поместите их на отдельные строки в том же порядке, но переместите вещь спереди в тело самого внутреннего цикла. Вот так, в общем:

thing for a in a_list for b in b_list for c in c_list

становится

for a in a_list:
    for b in b_list:
        for c in c_list:
            thing

за исключением того, что когда вы делаете выражением генератор, все things автоматически перейти в список или словарь или что угодно. В твоем случае,

dict((fn(i+1), code)
    for i, code in enumerate('FGHJKMNQUVXZ')
    for fn in (int, str))

становится

for i, code in enumerate('FGHJKMNQUVXZ'):
    for fn in (int, str):
        (fn(i+1), code)

кроме того, что все Кортежи будут преобразованы в dict.

как объясняют другие ответы, вы можете проследить выполнение этих двух for петли. Во-первых, внешний цикл устанавливает i to 0 и code to 'F', и внутри этого, внутренний цикл устанавливает fn to int а потом str, так что вы получите

int(0+1, 'F')
str(0+1, 'F')

после чего он переходит к следующему i и code.


Как видите,fn(i+1) будет вызван два раза. Первый int(i+1), второй str(i+1)

for a in ((fn(i+1), code)
                    for i, code in enumerate('FGH')
                    for fn in (int, str)):
    print a

выход:

(1, 'F')
('1', 'F')
(2, 'G')
('2', 'G')
(3, 'H')
('3', 'H')

упрощенный цикл для лучшей читаемости:

for i, code in enumerate('FGH'):
    for fn in (int, str):
        print i, code, fn

выход:

0 F <type 'int'>
0 F <type 'str'>
1 G <type 'int'>
1 G <type 'str'>
2 H <type 'int'>
2 H <type 'str'>

причина в этом (fn(i+1), code)) генератор дает кортеж с первым элементом как int или string, а второе значение как буква из 'FGHJKMNQUVXZ'

вот еще один пример без второго цикла

def gen_func(text):
    for i, code in enumerate(text):
        yield i, code
        yield str(i), code

print(dict(gen_func('FGHJKMNQUVXZ')))

все происходит с for fn in (int, str) это fn может быть либо встроенный int или str функция для преобразования значения i.

int(i)
str(i)

код использует это в именах классов Python, он также может использоваться для вызова функции конструктора, которая возвращает экземпляр этого класса. Например:

class A(object):
    def __init__(value):
        self.value = value

a = A(10)

отметим, что A - это имя класса, а также может использоваться как Python callable. Главное здесь заключается в том, что int и str может также использоваться таким же образом!

z = 10
value = int(z) # Returns number 10
print isinstance(value, int) # Prints True
print isinstance(value, str) # Prints False
value = str(z) # returns '10'
print isinstance(value, int) # Prints False
print isinstance(value, str) # Prints True

таким образом, второй цикл использует str и int как функции, возвращающие строку или целочисленное представление индекса в первом for петли. Вы также можете представить себе, что написали две такие функции:

def as_int(value):
    return int(value)

def as_str(value):
    return str(value)

, а затем for цикл вроде этого:

dict((fn(i+1), code)
for i, code in enumerate('FGHJKMNQUVXZ')
for fn in (as_int, as_str))
 # This second loop loops over the 2-element tuple 
 # whose contents are the two functions `as_int` and `as_str`

как долго-форма версия.