Liste Anlamada Çifte İterasyon


227

Python'da, bir liste kavrayışında, örneğin

[(x,y) for x in a for y in b]

bazı uygun diziler için a ve b. Python'un liste kavrayışlarının iç içe döngü semantiğinin farkındayım.

Sorum şu: Anlayıştaki bir yineleyici diğerine atıfta bulunabilir mi? Başka bir deyişle: Böyle bir şey alabilir miyim:

[x for x in a for a in b]

dış döngünün geçerli değeri iç iteratör nerede?

Örnek olarak, iç içe geçmiş bir listem varsa:

a=[[1,2],[3,4]]

bu sonucu elde etmek için liste anlama ifadesi ne olur:

[1,2,3,4]

?? (Lütfen sadece anlama cevaplarını listeleyin, çünkü öğrenmek istediğim şey budur).

Yanıtlar:


178

Sorunuzu kendi önerilerinizle cevaplamak için:

>>> [x for b in a for x in b] # Works fine

Liste anlama cevaplarını sorurken, mükemmel itertools.chain () 'i de belirtmeme izin verin:

>>> from itertools import chain
>>> list(chain.from_iterable(a))
>>> list(chain(*a)) # If you're using python < 2.6

11
[x for b in a for x in b]Bu her zaman python ile ilgili olarak hata yaptı. Bu sözdizimi çok geriye dönük. Genel biçimi x for x in yher zaman değişkeni doğrudan for'dan sonra alır, for'un solundaki ifadeye beslenir. Çift kavrama yaptığınız anda, en son yinelenen değişkeniniz aniden "uzak" olur. Garip ve doğal olarak hiç okumuyor
Cruncher

171

Umarım bu a,b,x,ybenim için çok anlam ifade etmediği için başka birine yardımcı olur ! Cümlelerle dolu bir metniniz olduğunu ve bir dizi kelime istediğinizi varsayalım.

# Without list comprehension
list_of_words = []
for sentence in text:
    for word in sentence:
       list_of_words.append(word)
return list_of_words

Liste kavramasını yatay olarak uzanan kod olarak düşünmeyi seviyorum.

Aşağıya bölmeyi deneyin:

# List Comprehension 
[word for sentence in text for word in sentence]

Misal:

>>> text = (("Hi", "Steve!"), ("What's", "up?"))
>>> [word for sentence in text for word in sentence]
['Hi', 'Steve!', "What's", 'up?']

Bu aynı zamanda jeneratörler için de geçerlidir

>>> text = (("Hi", "Steve!"), ("What's", "up?"))
>>> gen = (word for sentence in text for word in sentence)
>>> for word in gen: print(word)
Hi
Steve!
What's
up?

8
"Bilgisayar Bilimlerinde sadece iki zor sorun vardır: önbellek geçersiz kılma ve bir şeyleri adlandırma." - Phil Karlton
cezar

Bu, tüm sorunu daha az soyut hale getirdiği için harika bir cevaptır! Teşekkür ederim!
A. Blesius

Merak ediyordum, aynı şeyi bir liste kavrayışında üç soyutlama seviyesiyle yapabilir misiniz? Metin bölümleri, bölümlerdeki cümleler ve cümlelerdeki kelimeler gibi?
Kaptan Fogetti

123

Gee, sanırım cevaplayanı buldum: Hangi halkanın içte ve dışta olduğu konusunda yeterince ilgilenmiyordum. Liste kavrayışı şöyle olmalıdır:

[x for b in a for x in b]

istenen sonucu almak için ve evet, bir akım değeri bir sonraki döngü için yineleyici olabilir.


67
Liste anlama sözdizimi Python'un parıldayan noktalarından biri değildir.
Glenn Maynard

2
@Glenn Evet, basit ifadelerden daha fazlası için kolayca kıvrılıyor.
ThomasH

1
Ew. Bu liste anlayışları için "olağan" kullanım olduğundan emin değilim, ama zincirleme Python çok kötü olduğu için çok talihsiz.
Matt Joiner

14
Her bir 'for' satırından önce yeni satırlar koyarsanız çok temiz görünüyor.
Nick Garvey

16
Vay canına, bu kafamda mantıklı olanın tamamen tersi.
obskyr

51

Yineleyicilerin sırası karşı sezgisel görünebilir.

Örnek verelim: [str(x) for i in range(3) for x in foo(i)]

Ayrışalım:

def foo(i):
    return i, i + 0.5

[str(x)
    for i in range(3)
        for x in foo(i)
]

# is same as
for i in range(3):
    for x in foo(i):
        yield str(x)

4
Ne göz açıcı !!
nehem

Anladığım kadarıyla bunun nedeni, "listelenen ilk yineleme, kavrama döngüler için yuvalanmış olarak yazılırsa yazılacak en üst yinelemedir". Bunun mantıksız olmasının nedeni, OUTER döngüsünün (döngüler için iç içe yerleştirilmiş olarak yazıldıysa en üstteki) köşeli parantez içindeki liste / dikmenin (anlama nesnesi) İÇERİ'de görünmesidir. Tersine, INNER döngüsü (döngüler için iç içe olarak yazıldığında en içteki döngü) tam olarak bir kavramada en sağdaki döngüdür ve bu şekilde kavrayışın DIŞINDA görünür.
Zach Siegel

Soyut Elimizdeki yazılı [(output in loop 2) (loop 1) (loop 2)]olan (loop 1) = for i in range(3)ve (loop 2) = for x in foo(i):ve (output in loop 2) = str(x).
Qaswed

20

ThomasH zaten iyi bir cevap ekledi, ama ne olduğunu göstermek istiyorum:

>>> a = [[1, 2], [3, 4]]
>>> [x for x in b for b in a]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined

>>> [x for b in a for x in b]
[1, 2, 3, 4]
>>> [x for x in b for b in a]
[3, 3, 4, 4]

Sanırım Python liste kavrayışını soldan sağa ayrıştırıyor. Bu, ilk forolarak gerçekleşen ilk döngü yürütüleceği anlamına gelir .

Bunun ikinci "sorunu" b, liste kavrayışından "sızdı". İlk başarılı liste anlayışından sonra b == [3, 4].


3
İlginç bir nokta. Bu işte şaşırdı:x = 'hello'; [x for x in xrange(1,5)]; print x # x is now 4
grinch

2
Bu sızıntı Python 3'te giderildi: stackoverflow.com/questions/4198906/…
Denilson Sá Maia

10

Çok boyutlu diziyi tutmak istiyorsanız, dizi köşeli parantezleri iç içe yerleştirilmelidir. her öğeye bir kişinin eklendiği aşağıdaki örneğe bakın.

>>> a = [[1, 2], [3, 4]]

>>> [[col +1 for col in row] for row in a]
[[2, 3], [4, 5]]

>>> [col +1 for row in a for col in row]
[2, 3, 4, 5]

8

Bu bellek tekniği bana çok yardımcı oluyor:

[ <RETURNED_VALUE> <OUTER_LOOP1> <INNER_LOOP2> <INNER_LOOP3> ... <OPTIONAL_IF> ]

Ve şimdi düşünebilirim R eturn + O yalnızca uter-döngü R ight Ç rder

Yukarıda bilmek, 3 döngü için bile kapsamlı listedeki sipariş kolay görünüyor:


c=[111, 222, 333]
b=[11, 22, 33]
a=[1, 2, 3]

print(
  [
    (i, j, k)                            # <RETURNED_VALUE> 
    for i in a for j in b for k in c     # in order: loop1, loop2, loop3
    if i < 2 and j < 20 and k < 200      # <OPTIONAL_IF>
  ]
)
[(1, 11, 111)]

çünkü yukarıdaki sadece bir:

for i in a:                         # outer loop1 GOES SECOND
  for j in b:                       # inner loop2 GOES THIRD
    for k in c:                     # inner loop3 GOES FOURTH
      if i < 2 and j < 20 and k < 200:
        print((i, j, k))            # returned value GOES FIRST

bir iç içe liste / yapıyı yinelemek için teknik aynıdır: asorudan:

a = [[1,2],[3,4]]
[i2    for i1 in a      for i2 in i1]
which return [1, 2, 3, 4]

iç içe geçmiş

a = [[[1, 2], [3, 4]], [[5, 6], [7, 8, 9]], [[10]]]
[i3    for i1 in a      for i2 in i1     for i3 in i2]
which return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ve bunun gibi


Teşekkürler, ancak tarif ettiğiniz şey aslında ilgili yineleyicilerin bağımsız olduğu basit bir durumdur. Aslında, örneğinizde yineleyicileri herhangi bir sırayla kullanabilirsiniz ve aynı sonuç listesini (modulo sırası) alırsınız. Daha fazla ilgilendiğim durum, bir yineleyicinin diğerinin yinelenebilir hale geldiği iç içe listelerle ilgiliydi.
ThomasH

@ThomasH: kalın olarak tanımlanan döngü sırası tam olarak ihtiyacınıza göre. Alt kısımda verilerinizi kapsayan bir örnek ve ekstra iç içe düzeyle bir örnek daha eklendi.
Sławomir Lenart

5

Bunun anlaşılması daha kolay olduğunu düşünüyorum

[row[i] for row in a for i in range(len(a))]

result: [1, 2, 3, 4]

3

Ayrıca, o anda erişilen giriş listesinin üyesi ve bu üyenin içindeki öğe için de aynı değişkeni kullanabilirsiniz . Bununla birlikte, bu daha da (liste) anlaşılmaz hale getirebilir.

input = [[1, 2], [3, 4]]
[x for x in input for x in x]

Birincisi for x in input, girişin bir üye listesine götüren değerlendirilir, daha sonra Python for x in x, x değerinin erişmekte olduğu mevcut eleman tarafından üzerine yazıldığı ikinci bölüm boyunca yürür , sonra ilk olarak xne dönmek istediğimizi tanımlar.


1

Bu flatten_nlevel işlevi, bir seviyeye dönüştürmek için iç içe list1'i özyinelemeli olarak çağırır. Bunu dene

def flatten_nlevel(list1, flat_list):
    for sublist in list1:
        if isinstance(sublist, type(list)):        
            flatten_nlevel(sublist, flat_list)
        else:
            flat_list.append(sublist)

list1 = [1,[1,[2,3,[4,6]],4],5]

items = []
flatten_nlevel(list1,items)
print(items)

çıktı:

[1, 1, 2, 3, 4, 6, 4, 5]

1
Tamam, soru özellikle liste kavramasıyla ilgiliydi ve liste düzleme sadece bir örnekti. Ancak, genelleştirilmiş liste düzleştiricinizin kendisini tekrar tekrar çağırması gerektiğini varsayıyorum. Yani muhtemelen daha çok flatten_nlevel(sublist, flat_list), değil mi ?!
ThomasH
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.