Несколько циклов " 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
за исключением того, что когда вы делаете выражением генератор, все thing
s автоматически перейти в список или словарь или что угодно. В твоем случае,
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`
как долго-форма версия.