วิธี Pythonic เพื่อรวมคำสั่ง FOR สำหรับลูปและ IF


266

ฉันรู้วิธีใช้ทั้งลูปและ if statement ในบรรทัดแยกเช่น:

>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
...     if x in a:
...         print(x)
0,4,6,7,9

และฉันรู้ว่าฉันสามารถใช้ list comprehension เพื่อรวมสิ่งเหล่านี้เมื่อข้อความง่าย ๆ เช่น:

print([x for x in xyz if x in a])

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

for x in xyz if x not in a:
    print(x...)

นี่ไม่ใช่วิธีที่งูเหลือมควรจะใช้ใช่ไหม


23
นั่นคือวิธีที่มันคือ ... อย่าซับซ้อนเกินไปโดยพยายามทำให้มันง่ายขึ้น Pythonicไม่ได้หมายถึงการหลีกเลี่ยงการforวนซ้ำและifข้อความสั่งที่ชัดเจน
เฟลิกซ์คลิง

2
คุณสามารถใช้รายการที่สร้างในรายการความเข้าใจของคุณในแบบ for for loop นั่นอาจดูเหมือนตัวอย่างสุดท้ายของคุณ
จาค็อบ

ดังนั้นการประมวลผลลงวิธีที่เร็วที่สุดในการรวม a for loop เข้ากับคำสั่ง if ถ้าคำสั่ง if นั้นไม่รวมค่าที่จับคู่ไว้แล้วและรายการมีการเติบโตอย่างต่อเนื่องระหว่างการวนซ้ำของวนซ้ำ
ChewyChunks

3
@ Chewy โครงสร้างข้อมูลที่เหมาะสมจะทำให้โค้ดเร็วขึ้นไม่ใช่น้ำตาลทราย ตัวอย่างเช่นx in aช้าถ้าaเป็นรายการ
Nick Dandoulakis

1
นี่คือ Python ภาษาที่ตีความ; ทำไมทุกคนถึงคุยกันว่าโค้ดรวดเร็วแค่ไหน?
ArtOfWarfare

คำตอบ:


323

คุณสามารถใช้นิพจน์ตัวสร้างเช่นนี้:

gen = (x for x in xyz if x not in a)

for x in gen:
    print x

1
gen = (y for (x,y) in enumerate(xyz) if x not in a)ส่งกลับ >>> 12เมื่อฉันพิมพ์for x in gen: print x- แล้วทำไมพฤติกรรมที่ไม่คาดคิดกับแจกแจง?
ChewyChunks

9
เป็นไปได้ แต่ไม่ดีกว่าของเดิมสำหรับและถ้าบล็อก
Mike Graham

1
@ChewyChunks ที่จะใช้งานได้ แต่การเรียกให้แจงนับซ้ำซ้อน
Johnsyweb

132
ฉันคิดถึงหลามจริงๆที่สามารถพูดได้for x in xyz if x:
bgusach

10
for x in (x for x in xyz if x not in a):ใช้งานได้สำหรับฉัน แต่ทำไมคุณไม่ควรทำfor x in xyz if x not in a:ฉันไม่แน่ใจ ...
Matt Wenham

34

ตามThe Zen of Python (หากคุณสงสัยว่ารหัสของคุณเป็น "Pythonic" นั่นคือที่ที่ควรไป):

  • สวยดีกว่าน่าเกลียด
  • ชัดเจนดีกว่าโดยปริยาย
  • เรียบง่ายดีกว่าซับซ้อน
  • แบนดีกว่าซ้อนกัน
  • จำนวนการอ่าน

วิธี Pythonic ในการรับของทั้งสองคือ:sorted intersectionset

>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]

หรือองค์ประกอบเหล่านั้นxyzแต่ไม่ได้อยู่ในa:

>>> sorted(set(xyz).difference(a))
[12, 242]

แต่สำหรับลูปที่ซับซ้อนกว่านี้คุณอาจต้องการทำให้แบนโดยการวนซ้ำนิพจน์ตัวกำเนิดที่มีชื่อและ / หรือเรียกใช้ฟังก์ชันที่มีชื่อดี การพยายามที่จะปรับทุกอย่างในบรรทัดเดียวแทบจะไม่ "Pythonic"


อัปเดตการติดตามความคิดเห็นเพิ่มเติมเกี่ยวกับคำถามของคุณและคำตอบที่ยอมรับ

ฉันไม่แน่ใจว่าคุณกำลังพยายามทำอะไรenumerateแต่ถ้าaเป็นพจนานุกรมคุณอาจต้องการใช้ปุ่มเช่นนี้:

>>> a = {
...     2: 'Turtle Doves',
...     3: 'French Hens',
...     4: 'Colly Birds',
...     5: 'Gold Rings',
...     6: 'Geese-a-Laying',
...     7: 'Swans-a-Swimming',
...     8: 'Maids-a-Milking',
...     9: 'Ladies Dancing',
...     0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
...     print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
...     print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas

เสียงเหมือนจากความคิดเห็นด้านล่างฉันควรจะศึกษาเกี่ยวกับเครื่องกำเนิดไฟฟ้า ฉันไม่เคยใช้มัน ขอบคุณ เครื่องกำเนิดไฟฟ้าเร็วกว่าการรวมคำสั่ง FOR และ IF ที่เทียบเท่าหรือไม่ ฉันเคยใช้ชุด แต่บางครั้งองค์ประกอบที่ซ้ำซ้อนในรายการเป็นข้อมูลที่ฉันไม่สามารถทิ้งได้
ChewyChunks

@ ChewyChunks: กำเนิดไม่ได้เป็นวิธีเดียวที่จะเป็น Pythonic!
Johnsyweb

3
@Johnsyweb ถ้าคุณจะพูดถึง Zen of Python: "ควรมีอย่างใดอย่างหนึ่ง - และดีกว่าเพียงหนึ่ง - วิธีที่ชัดเจนที่จะทำมัน"
Wooble

@ Wooble: ควรมี ฉันอ้างถึงหัวข้อนั้นในคำตอบของฉันสำหรับคำถามอื่นในเวลาเดียวกัน!
Johnsyweb

18

โดยส่วนตัวฉันคิดว่านี่เป็นเวอร์ชั่นที่สวยที่สุด:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
  print x

แก้ไข

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

https://docs.python.org/2/library/operator.html#module-operator

from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))

4
filter(a.__contains__, xyz). โดยปกติเมื่อผู้คนใช้แลมบ์ดาพวกเขาต้องการสิ่งที่เรียบง่ายกว่า
Veky

ฉันคิดว่าคุณเข้าใจผิดบางอย่าง __contains__เป็นวิธีการที่เหมือนกันมีเพียงวิธีการพิเศษซึ่งหมายความว่าสามารถเรียกใช้โดยผู้ดำเนินการทางอ้อม ( inในกรณีนี้) แต่มันสามารถเรียกได้โดยตรงว่ามันเป็นส่วนหนึ่งของ API สาธารณะ ชื่อส่วนบุคคลมีการกำหนดไว้เป็นพิเศษโดยมีขีดล่างหนึ่งส่วนท้ายเพื่อให้ข้อยกเว้นสำหรับชื่อวิธีการพิเศษ - และพวกเขาจะต้องถูกตั้งชื่อเป็น mangling เมื่ออยู่ในขอบเขตของคลาส ดูdocs.python.org/3/reference/datamodel.html#specialnamesและdocs.python.org/3.6/tutorial/classes.html#private-variables
Veky

แน่นอนว่าใช้ได้ แต่การนำเข้าสองรายการเพื่ออ้างถึงวิธีการที่เข้าถึงได้โดยใช้แอตทริบิวต์ดูเหมือนว่าแปลก (ตัวดำเนินการมักใช้เมื่อการส่งคู่เป็นสิ่งจำเป็น แต่inถูกส่งตัวถูกดำเนินการโดยขวา) นอกจากนี้โปรดทราบว่าวิธีการoperatorส่งออกcontainsภายใต้ชื่อ__contains__นั้นจึงไม่ใช่ชื่อส่วนตัว ฉันคิดว่าคุณจะต้องเรียนรู้ที่จะอยู่กับความจริงที่ไม่ใช่ขีดเส้นใต้ทุกคู่หมายถึง "หลีกเลี่ยง" : -]
Veky

ฉันคิดว่าความlambdaต้องการของคุณจะรวมถึงnot:lambda w: not w in a, xyz
464

ตัวกรองดูเหมือนว่าสง่างามมากขึ้นโดยเฉพาะอย่างยิ่งสำหรับเงื่อนไขที่ซับซ้อนที่จะกลายเป็นฟังก์ชั่นที่กำหนดไว้แทน lambdas อาจจะตั้งชื่อฟังก์ชั่นแลมบ์ดาจะเพิ่มการอ่านบางเครื่องกำเนิดไฟฟ้าดูเหมือนว่าดีขึ้นเมื่อองค์ประกอบซ้ำมีการปรับเปลี่ยนบางอย่างเกี่ยวกับรายการรายการ
Khanis ร็อค

16

ต่อไปนี้คือการทำให้เข้าใจง่าย / ซับหนึ่งจากคำตอบที่ยอมรับ:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]

for x in (x for x in xyz if x not in a):
    print(x)

12
242

แจ้งให้ทราบว่าgeneratorถูกเก็บไว้แบบอินไลน์ นี่คือการทดสอบpython2.7และpython3.6 (สังเกตเห็น parens ในprint;))


10

ฉันอาจจะใช้:

for x in xyz: 
    if x not in a:
        print x...

@KirillTitov ใช่ python เป็นภาษาที่ไม่สามารถใช้งานได้จริง (นี่คือการเข้ารหัสที่มีความจำเป็นอย่างยิ่ง - และฉันเห็นด้วยกับผู้เขียนคำตอบนี้ว่าเป็นวิธีที่ตั้งค่าการเขียนของ Python ความพยายามในการใช้งานpythonicผลลัพธ์ฉันสามารถเขียนโค้ดตามหน้าที่ในทุกภาษาอื่น ๆ ที่ฉันใช้ (scala, kotlin, javascript, R, swift, .. ) แต่ยาก / อึดอัดใจใน python
javadba

9
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]  
set(a) & set(xyz)  
set([0, 9, 4, 6, 7])

Very Zen, @lazyr แต่จะไม่ช่วยฉันปรับปรุง block code ที่ซับซ้อนซึ่งขึ้นอยู่กับการวนซ้ำผ่านหนึ่งรายการและไม่สนใจองค์ประกอบที่ตรงกันในรายการอื่น มันเร็วกว่าที่จะจัดการกับรายการแรกเป็นชุดและเปรียบเทียบการรวม / ความแตกต่างกับรายการ "ละเว้น" ที่เพิ่มขึ้นหรือไม่?
ChewyChunks

ลองสิ่งนี้import time a = [2,3,4,5,6,7,8,9,0] xyz = [0,12,4,6,242,7,9] start = time.time() print (set(a) & set(xyz)) print time.time() - start
Kracekumar

@ChunChunks หากทั้งสองรายการเปลี่ยนไปในระหว่างการทำซ้ำมันอาจจะเร็วกว่าที่จะตรวจสอบแต่ละองค์ประกอบกับรายการที่ไม่ใช้ - ยกเว้นคุณควรทำให้มันเป็นชุดที่ไม่ใช้ if x in ignore: ...การตรวจสอบการเป็นสมาชิกในชุดเป็นอย่างมากอย่างรวดเร็ว:
Lauritz V. Thaulow

@lazyr ฉันเพิ่งเขียนรหัสโดยใช้การเพิกเฉยที่ตั้งอยู่เหนือรายชื่อที่ไม่ใช้ ปรากฏว่าประมวลผลเวลาช้าลงมาก (เพื่อความเป็นธรรมฉันเปรียบเทียบการใช้if set(a) - set(ignore) == set([]):ดังนั้นอาจเป็นเหตุผลที่ช้ากว่าการตรวจสอบสมาชิกฉันจะทดสอบอีกครั้งในอนาคตในตัวอย่างที่ง่ายกว่านี้มากกว่าที่ฉันเขียน
ChewyChunks

5

คุณสามารถใช้เครื่องกำเนิดไฟฟ้าได้เช่นกันหากการแสดงออกของเครื่องกำเนิดไฟฟ้ามีส่วนเกี่ยวข้องหรือซับซ้อนเกินไป:

def gen():
    for x in xyz:
        if x in a:
            yield x

for x in gen():
    print x

มันมีประโยชน์มากกว่าสำหรับฉันเล็กน้อย ฉันไม่เคยดูที่เครื่องกำเนิดไฟฟ้า พวกเขาฟังดูน่ากลัว (เพราะฉันเห็นพวกเขาในโมดูลที่โดยทั่วไปแล้วเจ็บปวดที่จะใช้)
ChewyChunks

2

ใช้intersectionหรือintersection_update

  • ทางแยก :

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    ans = sorted(set(a).intersection(set(xyz)))
  • intersection_update :

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    b = set(a)
    b.intersection_update(xyz)

    จากนั้นbเป็นคำตอบของคุณ


2

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

mylist = [1,2,3,4,5]
another_list = [2,3,4]

wanted = lambda x:x in another_list

for x in filter(wanted, mylist):
    print(x)

วิธีนี้มีประโยชน์สำหรับการแยกข้อกังวลหากฟังก์ชั่นเงื่อนไขเปลี่ยนแปลงรหัสเฉพาะซอที่มีคือฟังก์ชันนั้น

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

for x in filter(wanted, mylist):
    print(x)

กำเนิดวิธีดูเหมือนว่าดีขึ้นเมื่อคุณไม่ต้องการให้สมาชิกของรายการ แต่การปรับเปลี่ยนของสมาชิกกล่าวซึ่งดูเหมือนว่าพอดีมากขึ้นในการให้กำเนิด

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.5 for x in mylist if wanted(x))

for x in generator:
    print(x)

นอกจากนี้ตัวกรองยังทำงานร่วมกับเครื่องกำเนิดไฟฟ้าได้ด้วยแม้ว่าในกรณีนี้จะไม่มีประสิทธิภาพ

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.9 for x in mylist)

for x in filter(wanted, generator):
    print(x)

แต่แน่นอนว่ามันคงจะดีถ้าเขียนแบบนี้:

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

# for x in filter(wanted, mylist):
for x in mylist if wanted(x):
    print(x)

0

วิธีง่ายๆในการค้นหาองค์ประกอบทั่วไปที่เป็นเอกลักษณ์ของรายการ a และ b:

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.