ซ้ำสองครั้งในรายการความเข้าใจ


226

ใน Python คุณสามารถมีตัววนซ้ำหลายตัวในรายการความเข้าใจเช่น

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

สำหรับบางลำดับที่เหมาะสม a และ b ฉันตระหนักถึงความหมายของลูปซ้อนกันของความเข้าใจในรายการของไพ ธ อน

คำถามของฉันคือ: ตัววนซ้ำหนึ่งตัวในความเข้าใจสามารถอ้างถึงตัวอื่นได้หรือไม่? กล่าวอีกนัยหนึ่ง: ฉันขอสิ่งนี้ได้ไหม:

[x for x in a for a in b]

ที่ค่าปัจจุบันของลูปด้านนอกเป็นตัววนซ้ำของ Inner?

ตัวอย่างเช่นถ้าฉันมีรายการซ้อนอยู่:

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

การแสดงออกของรายการความเข้าใจจะเป็นอย่างไรเพื่อให้ได้ผลลัพธ์นี้:

[1,2,3,4]

?? (โปรดระบุเฉพาะคำตอบความเข้าใจเนื่องจากนี่คือสิ่งที่ฉันต้องการค้นหา)

คำตอบ:


178

หากต้องการตอบคำถามของคุณด้วยคำแนะนำของคุณเอง:

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

ในขณะที่คุณขอคำตอบรายการความเข้าใจให้ฉันยังชี้ itertools.chain ที่ยอดเยี่ยม ():

>>> 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]สิ่งนี้มีข้อบกพร่องเสมอเกี่ยวกับหลาม ไวยากรณ์นี้ย้อนกลับได้ รูปแบบทั่วไปของx for x in yเสมอมีตัวแปรโดยตรงหลังจาก for, ฟีดไปยังการแสดงออกทางด้านซ้ายของสำหรับ ทันทีที่คุณทำความเข้าใจสองครั้งตัวแปรที่วนซ้ำล่าสุดของคุณก็จะ "ไกล" ทันที มันอึดอัดใจและไม่อ่านตามธรรมชาติเลย
Cruncher

170

ฉันหวังว่านี่จะช่วยคนอื่นเนื่องจากa,b,x,yไม่มีความหมายกับฉันมากนัก! สมมติว่าคุณมีข้อความที่เต็มไปด้วยประโยคและคุณต้องการอาร์เรย์ของคำ

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

ฉันชอบคิดว่ารายการเข้าใจว่าเป็นการยืดโค้ดในแนวนอน

ลองแบ่งมันออกเป็น:

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

ตัวอย่าง:

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

นอกจากนี้ยังใช้งานได้กับเครื่องกำเนิดไฟฟ้า

>>> 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
"มีปัญหาอย่างหนักเพียงสองปัญหาในวิทยาการคอมพิวเตอร์: การทำให้ใช้ไม่ได้และการตั้งชื่อแคช" - ฟิล Karlton
cezar

นี่เป็นคำตอบที่ยอดเยี่ยมเพราะทำให้ปัญหาทั้งหมดเป็นนามธรรมน้อยลง! ขอบคุณ!
A. Blesius

ฉันสงสัยว่าคุณสามารถทำสิ่งเดียวกันกับสามระดับนามธรรมในรายการความเข้าใจได้หรือไม่? เช่นเดียวกับบทในข้อความประโยคในบทและคำในประโยค?
กัปตัน Fogetti

123

Gee ฉันคิดว่าฉันได้พบ anwser: ฉันไม่ได้สนใจมากพอเกี่ยวกับการวนรอบที่อยู่ด้านในและด้านนอก ความเข้าใจในรายการควรเป็นดังนี้:

[x for b in a for x in b]

เพื่อให้ได้ผลลัพธ์ที่ต้องการและใช่ค่าปัจจุบันหนึ่งค่าสามารถเป็นตัววนซ้ำสำหรับลูปถัดไป


67
รายการความเข้าใจทางไวยากรณ์ไม่ได้เป็นหนึ่งในจุดประกายของพี ธ
Glenn Maynard

2
@Glenn ใช่มันเป็นเรื่องง่ายที่จะได้รับมากกว่าการแสดงออกที่เรียบง่าย
ThomasH

1
Ew ฉันไม่แน่ใจว่านี่คือ "ปกติ" ใช้สำหรับความเข้าใจในรายการ แต่มันโชคร้ายมากที่การผูกมัดนั้นน่ารังเกียจใน Python
Matt Joiner

14
มันดูสะอาดมากถ้าคุณใส่บรรทัดใหม่ก่อนหน้า 'for' แต่ละอัน
Nick Garvey

16
ว้าวสิ่งนี้ตรงกันข้ามกับสิ่งที่ฉันคิดในใจ
obskyr

51

คำสั่งของตัววนซ้ำอาจดูเหมือนเคาน์เตอร์ง่าย

ยกตัวอย่างเช่น [str(x) for i in range(3) for x in foo(i)]

มาสลายมันกันเถอะ:

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
ช่างเป็นสิ่งที่เปิดตา !!
nehem

ความเข้าใจของฉันคือเหตุผลที่ว่านี่คือ "การทำซ้ำครั้งแรกที่ระบุไว้คือการทำซ้ำสูงสุดที่จะพิมพ์ถ้าความเข้าใจถูกเขียนเป็นซ้อนสำหรับลูป" เหตุผลนี้เป็นสิ่งที่ขัดกันเพราะวง OUTER (ด้านบนสุดถ้าเขียนเป็นซ้อนกันสำหรับลูป) จะปรากฏขึ้นที่ด้านในของรายการวงเล็บเหลี่ยม / dict (วัตถุ comprehension'ed) ในทางกลับกันอินเนอร์ลูป (ด้านในสุดเมื่อเขียนเป็นซ้อนกันสำหรับลูป) เป็นวงที่ถูกต้องที่สุดในความเข้าใจอย่างแม่นยำและอยู่ในลักษณะที่ปรากฏที่ด้านนอกของความเข้าใจ
Zach Siegel

เขียนเป็นนามธรรมที่เรามี[(output in loop 2) (loop 1) (loop 2)]กับ(loop 1) = for i in range(3)และและ(loop 2) = for x in foo(i): (output in loop 2) = str(x)
Qaswed

20

ThomasH ได้เพิ่มคำตอบที่ดีไปแล้ว แต่ฉันต้องการแสดงให้เห็นว่าเกิดอะไรขึ้น:

>>> 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]

ฉันเดา Python แยกวิเคราะห์รายการความเข้าใจจากซ้ายไปขวา ซึ่งหมายความว่าการforวนซ้ำครั้งแรกที่เกิดขึ้นจะถูกดำเนินการก่อน

"ปัญหา" ที่สองของสิ่งนี้คือการbทำให้ "รั่วไหล" ออกจากรายการความเข้าใจ b == [3, 4]หลังจากครั้งแรกเข้าใจรายการที่ประสบความสำเร็จ


3
จุดที่น่าสนใจ ฉันรู้สึกประหลาดใจที่นี้:x = 'hello'; [x for x in xrange(1,5)]; print x # x is now 4
grinch

2
การรั่วไหลนี้ได้รับการแก้ไขใน Python 3: stackoverflow.com/questions/4198906/…
Denilson Sá Maia

10

หากคุณต้องการเก็บอาร์เรย์หลายมิติหนึ่งควรซ้อนวงเล็บเหลี่ยม ดูตัวอย่างด้านล่างที่มีการเพิ่มองค์ประกอบลงในทุกองค์ประกอบ

>>> 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

เทคนิคหน่วยความจำนี้ช่วยฉันได้มาก:

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

และตอนนี้คุณสามารถคิดเกี่ยวกับRกลับสู่ + O uter วงเป็นเพียงR ight O rder

การทราบข้างต้นลำดับในรายการที่ครอบคลุมแม้จะเป็น 3 ลูปดูเหมือนง่าย


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)]

เพราะข้างต้นเป็นเพียง:

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

สำหรับการวนซ้ำรายการ / โครงสร้างที่ซ้อนกันเทคนิคก็เหมือนกัน: สำหรับaจากคำถาม:

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

สำหรับอีกระดับที่ซ้อนกัน

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]

และอื่น ๆ


ขอบคุณ แต่สิ่งที่คุณอธิบายเป็นกรณีง่าย ๆ ที่ผู้ทำซ้ำที่เกี่ยวข้องมีความเป็นอิสระ ในความเป็นจริงในตัวอย่างของคุณคุณสามารถใช้ตัววนซ้ำในลำดับใดก็ได้และจะได้รายการผลลัพธ์เดียวกัน (การสั่งซื้อแบบโมดูโล) กรณีที่ฉันสนใจมากขึ้นคือรายการที่ซ้อนกันซึ่งตัววนซ้ำหนึ่งตัวกลายเป็นตัววนซ้ำของรายการถัดไป
ThomasH

@ThomasH: ลำดับของลูปที่กำหนดเป็นตัวหนานั้นตรงกับความต้องการของคุณ ที่ด้านล่างได้เพิ่มตัวอย่างเพื่อให้ครอบคลุมข้อมูลของคุณและอีกหนึ่งตัวอย่างที่มีระดับซ้อนกันเป็นพิเศษ
Sławomir Lenart


3

นอกจากนี้คุณสามารถใช้ตัวแปรเดียวกันสำหรับสมาชิกของรายการอินพุตที่เข้าถึงได้ในปัจจุบันและสำหรับองค์ประกอบภายในสมาชิกนี้ อย่างไรก็ตามสิ่งนี้อาจทำให้ไม่สามารถเข้าใจได้มากขึ้น (รายการ)

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

อันดับแรกfor x in inputคือการประเมินซึ่งนำไปสู่รายการสมาชิกหนึ่งรายการจากนั้น Python จะเดินผ่านส่วนที่สองfor x in xในระหว่างที่ค่า x ถูกเขียนทับโดยองค์ประกอบปัจจุบันที่เข้าถึงอยู่จากนั้นองค์ประกอบแรกจะxกำหนดสิ่งที่เราต้องการคืน


1

ฟังก์ชัน flatten_nlevel นี้เรียกใช้ list1 ซ้อนกันแบบเรียกซ้ำเพื่อแปลงเป็นหนึ่งระดับ ลองใช้ดู

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)

เอาท์พุท:

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

1
ตกลงคำถามคือโดยเฉพาะเกี่ยวกับความเข้าใจในรายการและการทำให้รายการแบนราบเป็นเพียงตัวอย่างเท่านั้น แต่ฉันคิดว่าเครื่องปรับรายการทั่วไปของคุณจะต้องเรียกตัวเองซ้ำ ๆ ดังนั้นอาจเป็นเช่นflatten_nlevel(sublist, flat_list)นั้นใช่มั้ย!
ThomasH
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.