2010-08-03 41 views
12

Tôi đang tìm kiếm một giải pháp tốt hơn/Pythonic hơn cho đoạn sauLàm thế nào để đếm các phần tử không null trong một lần lặp?

count = sum(1 for e in iterable if e) 
+7

có gì sai hoặc 'không theo chủ nghĩa' theo cách của bạn? –

+1

Biểu thức máy phát điện là Pythonic. Bạn chỉ có nghĩa là ngắn hơn? –

+0

OP, hãy nhận biết chính xác những gì được và không phải là không null, bằng Python. Ví dụ 'tổng (1 cho e trong [Sai, 0, '', [],(), [''], (''), [Sai], (Sai,), Không có] nếu e)' thực sự đánh giá đến 3 thay vì 0, bởi vì các chuỗi lồng nhau '[['']]', '[[False]]', '[(False,)]' được tính là không null, nhưng '[('')] 'không, [! Dù sao nói chung, mã bạn đưa ra là tốt và đầy đủ cho công việc, nếu chúng tôi được đảm bảo danh sách là bằng phẳng. – smci

Trả lời

18
len(filter(None, iterable)) 

Sử dụng None làm vị từ cho filter chỉ nói để sử dụng tính trung thực của các mục. (Có thể rõ ràng hơn sẽ len(filter(bool, iterable)))

+0

+1 Cái này đẹp và nhanh (khoảng 8x nhanh hơn biểu thức máy phát trên máy tính của tôi). 'bool' có vẻ hơi chậm hơn' None' –

+10

Sử dụng bộ nhớ bổ sung O (N). –

+1

Giải pháp nhanh, nhưng bộ nhớ phụ O (N) là trừ. Danh sách của tôi thường có 1000000 phần tử để sử dụng bộ nhớ 1MB chỉ để đếm các phần tử khác không phải là không hiệu quả. Ngoài ra, tôi không nghĩ rằng bạn đã hẹn giờ gc để lấy lại danh sách được tạo bởi bộ lọc. – Alexandru

3

Thành thực mà nói, tôi không thể nghĩ ra một cách tốt hơn để làm điều đó hơn những gì bạn đã có.

Vâng, tôi đoán mọi người có thể tranh luận về "tốt hơn", nhưng tôi nghĩ bạn không thể tìm thấy bất kỳ điều gì ngắn hơn, đơn giản hơn và rõ ràng hơn.

+0

Có. Cảm ơn bạn. – ars

0

Đây không phải là nhanh nhất, nhưng có lẽ có ích cho mã golf

sum(map(bool, iterable)) 
+1

Sử dụng bộ nhớ bổ sung O (N). –

+0

Chỉ trong Python 2.x - trong Python 3, bản đồ trả về một trình tạo, không phải danh sách. – PaulMcG

+0

Ngoài ra bạn có thể sử dụng imap từ itertools. –

2
sum(not not e for e in iterable) 
+1

Chà. Câu trả lời này đủ lớn để 'bool (e)' không tồn tại. –

3

Hầu hết Pythonic là viết một hàm phụ trợ nhỏ và đặt nó trong đáng tin cậy của bạn "tiện ích" mô-đun (hoặc submodule của gói thích hợp, khi bạn có đủ ;-):

import itertools as it 

def count(iterable): 
    """Return number of items in iterable.""" 
    return sum(1 for _ in iterable) 

def count_conditional(iterable, predicate=None): 
    """Return number of items in iterable that satisfy the predicate.""" 
    return count(it.ifilter(predicate, iterable)) 

Chính xác cách bạn chọn để thực hiện thứ Các tiện ích này ít quan trọng hơn (bạn có thể chọn bất kỳ lúc nào để mã hóa một số trong Cython, ví dụ, nếu một số hồ sơ trên một ứng dụng sử dụng các tiện ích cho thấy nó hữu ích): điều quan trọng là có chúng như thư viện tiện ích hữu ích của riêng bạn chức năng, với những cái tên và các mẫu gọi bạn thích, để làm hết sức quan trọng ứng dụng mức mã của bạn rõ ràng hơn, dễ đọc hơn, và ngắn gọn hơn là nếu bạn nhét nó đầy vặn vẹo inlined -!)

+0

"các mẫu gọi [mà] bạn thích": người đọc mã không có tác giả có thể được mong đợi biết (ví dụ) 'tổng (bản đồ (bool, iterable))' không yêu cầu SO hoặc tra cứu TFM ... để tìm định nghĩa ở một nơi khác, hãy phá vỡ luồng đọc. –

0

Nếu bạn' chỉ đang cố gắng xem liệu có thể lặp lại được không, có thể điều này sẽ giúp ích:

def is_iterable_empty(it): 
    try: 
     iter(it).next() 
    except StopIteration: 
     return True 
    else: 
     return False 

các câu trả lời khác sẽ mất thời gian O (N) để hoàn thành (và một số mất bộ nhớ O (N); mắt tốt, John!). Hàm này mất thời gian O (1). Nếu bạn thực sự cần độ dài, thì các câu trả lời khác sẽ giúp bạn nhiều hơn.

+0

Ummm tốt (1) điều này sẽ ngắn hơn: 'is_iterable_empty = lambda it: not any (it)' (2) Điều này không tiêu thụ phần tử đầu tiên nếu iterable không rỗng và là một iterator? –

+0

Thực ra, giải pháp của bạn với hàm 'any' sẽ không thực sự làm điều tương tự trong mọi trường hợp. Nếu trình vòng lặp chỉ trả về các đối tượng boolean 'False' (hoặc các danh sách trống, vv), thì lambda của bạn sẽ cho kết quả dương tính giả. Tôi chắc chắn rằng mã này có thể được rút ngắn, nhưng tôi thích đưa ra mã mở rộng cho các cuộc biểu tình. Đối với việc tiêu thụ đối tượng đầu tiên, có, nó sẽ, nhưng tất cả các giải pháp ở đây sẽ tiêu thụ ít nhất một phần tử (tiêu thụ nhiều nhất). Không có cách nào tốt để giải quyết vấn đề này, nhưng miễn là nó được ghi lại, thì mọi thứ sẽ ổn thôi. –

0

Có lẽ cách Pythonic nhất là viết mã không cần tính năng đếm.

Thông thường nhanh nhất là viết kiểu của các hàm bạn đang làm tốt nhất và tiếp tục tinh chỉnh kiểu của bạn.

Viết một lần đọc thường mã.

Bằng cách mã của bạn không làm những gì tiêu đề của bạn nói! Để đếm không phải 0 các yếu tố không phải là đơn giản xem xét làm tròn sai sót trong số nổi, mà sai là 0 ..

Nếu bạn chưa nổi giá trị điểm trong danh sách, điều này có thể làm điều đó:

def nonzero(seq): 
    return (item for item in seq if item!=0) 

seq = [None,'', 0, 'a', 3,[0], False] 
print seq,'has',len(list(nonzero(seq))),'non-zeroes' 

print 'Filter result',len(filter(None, seq)) 

"""Output: 
[None, '', 0, 'a', 3, [0], False] has 5 non-zeroes 
Filter result 3 
""" 
+0

Không phải vậy, đó chỉ là một cách phức tạp để nói 'len (danh sách (mục cho mục trong seq nếu mục! = 0)) '. Đó là thực sự ** ít Pythonic **. Không cần 'nonzero (seq)' để xây dựng danh sách trung gian throwaway khi tất cả những gì chúng ta muốn làm là đếm 1 cho dù một item có phải là nonzero hay không. Làm điều đó bên trong một biểu thức máy phát có nghĩa là chúng ta không cần phải xây dựng một danh sách trung gian, AFAIK. – smci

0

Đây là một giải pháp với thời gian chạy O (n) và O (1) bộ nhớ bổ sung:

count = reduce(lambda x,y:x+y, imap(lambda v: v>0, iterable)) 

Hy vọng điều đó sẽ hữu ích!

Các vấn đề liên quan