Cách Pythonic để kết hợp vòng lặp FOR và câu lệnh IF


266

Tôi biết cách sử dụng cả hai cho các vòng lặp và nếu các câu lệnh trên các dòng riêng biệt, chẳng hạn như:

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

Và tôi biết rằng tôi có thể sử dụng một sự hiểu biết danh sách để kết hợp những điều này khi các câu lệnh đơn giản, chẳng hạn như:

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

Nhưng những gì tôi không thể tìm thấy là một ví dụ tốt ở bất cứ đâu (để sao chép và học hỏi) thể hiện một tập lệnh phức tạp (không chỉ là "in x") xảy ra sau sự kết hợp của vòng lặp for và một số câu lệnh if. Một cái gì đó mà tôi mong đợi trông giống như:

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

Đây có phải chỉ là cách con trăn được cho là hoạt động?


23
Đó là cách nó ... không làm quá nhiều thứ bằng cách cố gắng đơn giản hóa chúng. Pythonic không có nghĩa là tránh mọi forvòng lặp và iftuyên bố rõ ràng .
Felix Kling

2
Bạn có thể sử dụng danh sách được tạo trong phần hiểu danh sách của bạn trong một vòng lặp for. Điều đó sẽ giống như ví dụ cuối cùng của bạn.
Jacob

Vì vậy, bắt đầu xử lý, cách nhanh nhất để kết hợp vòng lặp for với câu lệnh if, nếu câu lệnh if loại trừ các giá trị đã được khớp và danh sách sẽ tiếp tục tăng trong vòng lặp của vòng lặp for?
ChewyChunks

3
@Chewy, cấu trúc dữ liệu phù hợp sẽ làm cho mã nhanh hơn, không phải là cú pháp đường. Ví dụ, x in alà chậm nếu alà một danh sách.
Nick Dandoulakis

1
Đây là Python, một ngôn ngữ được giải thích; Tại sao có ai thảo luận về mã nhanh như thế nào?
ArtOfWarfare

Câu trả lời:


323

Bạn có thể sử dụng các biểu thức trình tạo như thế này:

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)trả về >>> 12khi tôi gõ for x in gen: print x- vậy tại sao hành vi không mong muốn với liệt kê?
ChewyChunks

9
Có thể, nhưng không đẹp hơn các khối ban đầu và nếu.
Mike Graham

1
@ChewyChunks. Điều đó sẽ làm việc nhưng cuộc gọi để liệt kê là dư thừa.
Johnsyweb

132
Tôi thực sự nhớ con trăn có thể nóifor x in xyz if x:
bgusach

10
for x in (x for x in xyz if x not in a):làm việc cho tôi, nhưng tại sao bạn không thể làm được for x in xyz if x not in a:, tôi không chắc ...
Matt Wenham

34

Theo Zen của Python (nếu bạn đang tự hỏi liệu mã của mình có phải là "Pythonic" hay không, đó là nơi cần đến):

  • Đẹp thì tốt hơn xấu.
  • Rõ ràng là tốt hơn so với ngầm.
  • Đơn giản là tốt hơn phức tạp.
  • Bằng phẳng là tốt hơn so với lồng nhau.
  • Tính dễ đọc.

Cách Pythonic để có được hai s là:sorted intersectionset

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

Hoặc những yếu tố đó xyznhưng không có trong a:

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

Nhưng đối với một vòng lặp phức tạp hơn, bạn có thể muốn làm phẳng nó bằng cách lặp qua biểu thức trình tạo có tên tốt và / hoặc gọi ra một hàm có tên tốt. Cố gắng để phù hợp với tất cả mọi thứ trên một dòng hiếm khi là "Pythonic".


Cập nhật sau nhận xét bổ sung về câu hỏi của bạn và câu trả lời được chấp nhận

Tôi không chắc chắn những gì bạn đang cố gắng làm với enumerate, nhưng nếu alà một từ điển, bạn có thể muốn sử dụng các phím, như thế này:

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

Âm thanh như từ các ý kiến ​​dưới đây, tôi nên nghiên cứu về máy phát điện. Tôi chưa bao giờ sử dụng chúng. Cảm ơn. Là một trình tạo nhanh hơn sự kết hợp tương đương của các câu lệnh FOR và IF? Tôi cũng đã sử dụng các bộ, nhưng đôi khi các yếu tố dư thừa trong danh sách là thông tin tôi không thể loại bỏ.
ChewyChunks

@ChewyChunks: Máy phát điện không phải là cách duy nhất để trở thành Pythonic!
Johnsyweb

3
@Johnsyweb, nếu bạn định trích dẫn Zen của Python: "Nên có một-- và tốt nhất là chỉ có một cách - rõ ràng để làm điều đó."
Wooble

@Wooble: Có nên. Tôi đã trích dẫn phần đó trong câu trả lời của tôi cho một câu hỏi khác cùng một lúc!
Johnsyweb

18

Cá nhân tôi nghĩ rằng đây là phiên bản đẹp nhất:

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

Biên tập

nếu bạn rất muốn tránh sử dụng lambda, bạn có thể sử dụng ứng dụng chức năng một phần và sử dụng mô đun toán tử (cung cấp chức năng của hầu hết các toán tử).

https://docs.python.org/2/l Library / operator.html # model-operator

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

4
filter(a.__contains__, xyz). Thông thường khi mọi người sử dụng lambda, họ thực sự cần một cái gì đó đơn giản hơn nhiều.
Veky 18/07/2015

Tôi nghĩ rằng bạn đã hiểu nhầm một cái gì đó. __contains__là một phương thức giống như bất kỳ phương thức nào khác, chỉ có nó là một phương thức đặc biệt , có nghĩa là nó có thể được gọi một cách gián tiếp bởi một toán tử ( introng trường hợp này). Nhưng nó cũng có thể được gọi trực tiếp, nó là một phần của API công khai. Các tên riêng được định nghĩa cụ thể là có nhiều nhất một dấu gạch dưới, để cung cấp ngoại lệ cho các tên phương thức đặc biệt - và chúng có thể được đặt tên xáo trộn khi theo từ vựng trong phạm vi lớp. Xem docs.python.org/3/reference/datamodel.html#specialnamesdocs.python.org/3.6/tutorial/groupes.html#private-variables .
Veky

Điều này chắc chắn là ổn, nhưng hai lần nhập chỉ để có thể tham khảo một phương thức có thể truy cập bằng cách chỉ sử dụng một thuộc tính có vẻ kỳ lạ (toán tử thường được sử dụng khi điều phối kép là cần thiết, nhưng inđược gửi đơn giản là toán hạng bên phải). Bên cạnh đó, lưu ý rằng operatorcũng xuất khẩu containsphương thức dưới tên __contains__, vì vậy nó chắc chắn không phải là một tên riêng. Tôi nghĩ rằng bạn sẽ phải học cách sống với thực tế rằng không phải mọi dấu gạch dưới đôi đều có nghĩa là "tránh xa". : -]
Veky

Tôi nghĩ rằng bạn lambdacần sửa chữa để bao gồm not: lambda w: not w in a, xyz
javadba

Bộ lọc có vẻ thanh lịch hơn, đặc biệt đối với các điều kiện phức tạp sẽ trở thành các hàm được xác định thay vì lambdas, có thể đặt tên hàm lambda sẽ thêm một số khả năng đọc, Trình tạo có vẻ tốt hơn khi các phần tử lặp là một số sửa đổi trên các mục danh sách
Khanis Rok

16

Sau đây là đơn giản hóa / một lớp lót từ câu trả lời được chấp nhận:

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

Lưu ý rằng generatorđã được giữ nội tuyến . Điều này đã được thử nghiệm trên python2.7python3.6 (chú ý các dấu ngoặc trong print;))


10

Tôi có thể sẽ sử dụng:

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

@KirillTitov Có python là một ngôn ngữ không có chức năng cơ bản (đây là một mã hóa hoàn toàn bắt buộc - và tôi đồng ý với tác giả của câu trả lời này rằng đó là cách python được thiết lập để viết. Cố gắng sử dụng các chức năng dẫn đến việc đọc kém hoặc không- pythonicTôi có thể viết mã theo chức năng trong mọi ngôn ngữ khác mà tôi sử dụng (scala, kotlin, javascript, R, swift, ..) nhưng khó / lúng túng trong 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])

Rất Zen, @lazyr, nhưng sẽ không giúp tôi cải thiện khối mã phức tạp phụ thuộc vào việc lặp qua một danh sách và bỏ qua các yếu tố phù hợp trong danh sách khác. Có nhanh hơn khi coi danh sách đầu tiên là một tập hợp và so sánh sự kết hợp / khác biệt với danh sách "bỏ qua" thứ hai đang gia tăng không?
ChewyChunks

Hãy thử điều nàyimport 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

@ChewyChunks nếu một trong hai danh sách thay đổi trong quá trình lặp, có thể sẽ nhanh hơn để kiểm tra từng thành phần so với danh sách bỏ qua - ngoại trừ bạn nên đặt nó thành một tập hợp bỏ qua. Kiểm tra tư cách thành viên theo bộ rất nhanh : if x in ignore: ....
Lauritz V. Thaulow

@lazyr Tôi chỉ viết lại mã của mình bằng cách sử dụng bộ bỏ qua trên danh sách bỏ qua. Xuất hiện để xử lý thời gian chậm hơn nhiều. (Công bằng mà nói, tôi đã so sánh việc sử dụng if set(a) - set(ignore) == set([]):nên có lẽ đó là lý do tại sao nó chậm hơn nhiều so với việc kiểm tra tư cách thành viên. Tôi sẽ kiểm tra lại điều này trong tương lai với một ví dụ đơn giản hơn nhiều so với những gì tôi đang viết.
ChewyChunks

5

Bạn cũng có thể sử dụng trình tạo , nếu các biểu thức của trình tạo trở nên quá liên quan hoặc phức tạp:

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

for x in gen():
    print x

Điều này hữu ích hơn một chút với tôi. Tôi chưa bao giờ nhìn vào máy phát điện. Chúng nghe có vẻ đáng sợ (vì tôi thấy chúng trong các mô-đun nói chung là một nỗi đau để sử dụng).
ChewyChunks

2

Sử dụng intersectionhoặcintersection_update

  • ngã tư :

    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)))
  • giao lộ_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)

    sau đó blà câu trả lời của bạn


2

Tôi thích câu trả lời của Alex , vì bộ lọc chính xác là nếu được áp dụng cho danh sách, vì vậy nếu bạn muốn khám phá một tập hợp con của danh sách được cung cấp một điều kiện, đây có vẻ là cách tự nhiên nhất

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)

phương pháp này hữu ích cho việc phân tách các mối quan tâm, nếu hàm điều kiện thay đổi, mã duy nhất để tìm hiểu là chính hàm đó

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

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

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

Các máy phát điện phương pháp có vẻ tốt hơn khi bạn không muốn các thành viên của danh sách, nhưng sự thay đổi của các thành viên cho biết, mà dường như nhiều hơn phù hợp với một máy phát điện

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)

Ngoài ra, các bộ lọc hoạt động với máy phát điện, mặc dù trong trường hợp này nó không hiệu quả

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)

Nhưng tất nhiên, vẫn sẽ rất tuyệt nếu viết như thế này:

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

Một cách đơn giản để tìm các yếu tố phổ biến duy nhất của danh sách a và b:

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.