Linha única aninhada para loops

102

Escreveu esta função em python que transpõe uma matriz:

def transpose(m):
    height = len(m)
    width = len(m[0])
    return [ [ m[i][j] for i in range(0, height) ] for j in range(0, width) ]

No processo, percebi que não entendo totalmente como os loops for aninhados de linha única são executados. Ajude-me a entender respondendo às seguintes perguntas:

  1. Qual é a ordem em que este loop for é executado?
  2. Se eu tivesse um loop for aninhado triplo, que ordem ele executaria?
  3. O que seria igual ao loop for igual não aninhado?

Dado,

[ function(i,j) for i,j in object ]
  1. Que tipo de objeto deve ser para usar esta estrutura de loop for?
  2. Qual é a ordem em que i e j são atribuídos aos elementos no objeto?
  3. Pode ser simulado por uma estrutura diferente de loop for?
  4. Este loop for pode ser aninhado com uma estrutura for loop semelhante ou diferente? E como ficaria?

Informações adicionais são apreciadas também.

Asher Garland
fonte

Respostas:

169

A melhor fonte de informações é o tutorial oficial do Python sobre compreensão de listas . As compreensões de lista são quase as mesmas que os loops for (certamente qualquer compreensão de lista pode ser escrita como um loop for), mas geralmente são mais rápidas do que usar um loop for.

Veja esta compreensão de lista mais longa do tutorial (a ifparte filtra a compreensão, apenas as partes que passam na instrução if são passadas para a parte final da compreensão de lista (aqui (x,y)):

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

É exatamente igual a este loop for aninhado (e, como diz o tutorial, observe como a ordem de for e if são as mesmas).

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

A principal diferença entre uma compreensão de lista e um loop for é que a parte final do loop for (onde você faz algo) vem no início e não no final.

Para suas perguntas:

Que tipo de objeto deve ser para usar esta estrutura de loop for?

Um iterável . Qualquer objeto que pode gerar um conjunto (finito) de elementos. Isso inclui qualquer recipiente, listas, conjuntos, geradores, etc.

Qual é a ordem em que i e j são atribuídos aos elementos no objeto?

Eles são atribuídos exatamente na mesma ordem em que são gerados a partir de cada lista, como se estivessem em um loop for aninhado (para sua primeira compreensão, você obteria 1 elemento para i, então, cada valor de j, 2º elemento em i, então, cada valor de j, etc.)

Pode ser simulado por uma estrutura diferente de loop for?

Sim, já mostrado acima.

Este loop for pode ser aninhado com uma estrutura for loop semelhante ou diferente? E como ficaria?

Claro, mas não é uma boa ideia. Aqui, por exemplo, fornece uma lista de listas de personagens:

[[ch for ch in word] for word in ("apple", "banana", "pear", "the", "hello")]
Jeff Tratner
fonte
Eu me pergunto o que orientou sua escolha de ordenação no aninhamento duplo. Acho a outra forma mais natural (para y do que para x no seu exemplo). Acabei de perceber isso depois de 3 anos fazendo python (não extensivamente, mas ainda ...) e usando esses tipos de loops !!
Thomas,
@Thomas também acho o outro caminho mais intuitivo. A escolha que acredito foi puramente conveniente. Fazê-lo da maneira mais intuitiva significaria ter que lidar com símbolos não resolvidos até que os encontrasse mais tarde na declaração. Tente analisar para cada banana.peel em cada banana para cada city.bananastore em cada cidade no papel. Não é tão fácil. Porém, o contrário é fácil e agradável.
Pyjong,
29

Você pode estar interessado em itertools.product, que retorna um rendimento iterável de tuplas de valores de todos os iteráveis ​​que você passa. Ou seja, itertools.product(A, B)produz todos os valores da forma (a, b), de onde avêm Aos bvalores e de onde vêm os valores B. Por exemplo:

import itertools

A = [50, 60, 70]
B = [0.1, 0.2, 0.3, 0.4]

print [a + b for a, b in itertools.product(A, B)]

Isso imprime:

[50.1, 50.2, 50.3, 50.4, 60.1, 60.2, 60.3, 60.4, 70.1, 70.2, 70.3, 70.4]

Observe como o argumento final passado itertools.producté o "interno". Geralmente, é igual aitertools.product(a0, a1, ... an)[(i0, i1, ... in) for in in an for in-1 in an-1 ... for i0 in a0]

Lynn
fonte
4

Em primeiro lugar, seu primeiro código não usa um loop for per se, mas uma compreensão de lista .

  1. Seria equivalente a

    para j no intervalo (0, largura): para i no intervalo (0, altura): m [i] [j]

  2. Da mesma forma, geralmente se aninha como for loops, da direita para a esquerda. Mas a sintaxe de compreensão de lista é mais complexa.

  3. Não tenho certeza do que esta pergunta está perguntando


  1. Qualquer objeto iterável que produza objetos iteráveis ​​que produzem exatamente dois objetos (que bocado - isto é, [(1,2),'ab']seria válido)

  2. A ordem em que o objeto cede na iteração. ivai para o primeiro rendimento, jo segundo.

  3. Sim, mas não tão bonito. Acredito que seja funcionalmente equivalente a:

    l = lista ()
    para i, j no objeto:
        l.append (função (i, j))
    

    ou melhor ainda, use o mapa :

    map(function, object)

    Mas é claro que a função teria que se tornar i, por jsi só.

  4. Não é a mesma pergunta que 3?

príncipe koryl
fonte
2

Você pode usar dois loops for na mesma linha usando a zipfunção

Código:

list1 = ['Abbas', 'Ali', 'Usman']
list2 = ['Kamran', 'Asgar', 'Hamza', 'Umer']
list3 = []
for i,j in zip(list1,list2):
    list3.append(i)
    list3.append(j)
print(list3)

Resultado:

['Abbas', 'Kamran', 'Ali', 'Asgar', 'Usman', 'Hamza']

Então, usando a função zip, podemos usar dois loops for ou podemos iterar duas listas na mesma linha.

Muhammad Abbas
fonte
-1

Código abaixo para melhores exemplos de loops aninhados, enquanto usa dois loops for, lembre-se de que a saída do primeiro loop é entrada para o segundo loop. O término do loop também é importante ao usar os loops aninhados

for x in range(1, 10, 1):
     for y in range(1,x):
             print y,
        print
OutPut :
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8
rameshbabu reddy
fonte