Несколько циклов " 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`
как долго-форма версия.