2016-12-27 4 views
14

В this Python documentation следующее используется в качестве примера выражения генератора:Multiple «для» петли в словаре генератора

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 в строка и добавляет дополнительную запись в словарь.

Я нашел this Stack Overflow question, но я все еще не мог понять, как работает цикл for.

ответ

2

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

(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 (обеспечивается наружной петли) не изменились, но fn=str, поэтому новый кортеж, переданный конструктору словаря, составляет

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

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

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

и т.д. и т.п.

3

Код использует это в именах классов 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` 

В долгосрочной форме версии примера.

4

Причины в том, из-за этого (fn(i+1), code)) генератор дает кортеж с первым элементом либо как Int или строкой и второе значение в качестве письма от '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) 
5

Как вы можете видеть, 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'> 
9

Это может помочь «развернуть» петли в выражении генератора и записать их как независимые петли 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 в 0 и code к 'F', и в том, что внутренний цикл устанавливает fn к int, а затем str, так что вы получите

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

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

+0

мне это нравится способ объяснения путем преобразования кода! Если бы это был я, я мог бы использовать только два уровня в общем вступлении, чтобы оставаться близко к вопросу, а также избегать 'a_list' в пользу' a_seq' или 'a_iterable' (список такой конкретный) - но это не я; -) – Dilettant

+0

@ Dilettant Я хотел использовать другое количество уровней, чтобы показать, как техника обобщается. Вы правы, что что-то вроде 'a_iter', вероятно, было бы лучшим именем, но я полагал, что это не слишком важно, потому что ученики на этом уровне не слишком обеспокоены различиями между списками и итерами. –

+0

Спасибо за продуманную обратную связь, и я разделяю намерение, поскольку обобщение - это именно то, что есть - я часто удивляюсь, что именно попадает в глаза учащегося и что происходит от него - особенно. при обучении :-) – Dilettant

 Смежные вопросы

  • Нет связанных вопросов^_^