Tại sao việc sử dụng len (SEQUENCE) trong các giá trị điều kiện được Pylint xem là không chính xác?


211

Xem xét đoạn mã này:

from os import walk

files = []
for (dirpath, _, filenames) in walk(mydir):
    # more code that modifies files
if len(files) == 0: # <-- C1801
    return None

Tôi đã bị báo động bởi Pylint với thông báo này liên quan đến dòng với câu lệnh if:

[pylint] C1801: Không sử dụng len(SEQUENCE)làm giá trị điều kiện

Quy tắc C1801, thoạt nhìn, nghe có vẻ không hợp lý lắm và định nghĩa trên hướng dẫn tham khảo không giải thích được tại sao đây là vấn đề. Trong thực tế, nó hoàn toàn gọi nó là sử dụng không chính xác .

len-as-condition (C1801) : Không sử dụng len(SEQUENCE)làm giá trị điều kiện Được sử dụng khi Pylint phát hiện việc sử dụng len (chuỗi) không chính xác trong các điều kiện.

Những nỗ lực tìm kiếm của tôi cũng đã thất bại trong việc cung cấp cho tôi một lời giải thích sâu sắc hơn. Tôi hiểu rằng thuộc tính độ dài của chuỗi có thể được đánh giá một cách lười biếng và __len__có thể được lập trình để có tác dụng phụ, nhưng có một câu hỏi đặt ra là liệu một mình có đủ vấn đề để Pylint gọi việc sử dụng đó không chính xác hay không. Do đó, trước khi tôi đơn giản cấu hình dự án của mình để bỏ qua quy tắc, tôi muốn biết liệu tôi có thiếu điều gì trong lý luận của mình không.

Khi nào việc sử dụng len(SEQ)như là một giá trị điều kiện có vấn đề? Những tình huống chính nào mà Pylint đang cố gắng tránh với C1801?


9
Bởi vì bạn có thể đánh giá tính trung thực của chuỗi trực tiếp. pylint muốn bạn làm if files:hoặcif not files:
Patrick Haugh

38
lenkhông biết bối cảnh mà nó được gọi, vì vậy nếu tính toán độ dài có nghĩa là đi qua toàn bộ chuỗi, thì nó phải; nó không biết rằng kết quả chỉ được so sánh với 0. Việc tính toán giá trị boolean có thể dừng lại sau khi nó nhìn thấy phần tử đầu tiên, bất kể chuỗi thực sự dài bao nhiêu. Tôi nghĩ rằng pylint đang được một chút ý kiến ​​ở đây, mặc dù; Tôi không thể nghĩ về bất kỳ tình huống sử dụng sailen , chỉ là đó là một lựa chọn tồi tệ hơn so với giải pháp thay thế.
chepner

2
@ E_net4 Tôi nghĩ rằng PEP-8 có lẽ là nơi để bắt đầu.
Patrick Haugh


6
SEQUENCES cần một 'trống ()' hoặc 'isempty ()' như C ++ imo.
JDonner

Câu trả lời:


281

Khi nào việc sử dụng len(SEQ)như là một giá trị điều kiện có vấn đề? Những tình huống chính nào mà Pylint đang cố gắng tránh với C1801?

Nó không thực sự có vấn đề khi sử dụng len(SEQUENCE)- mặc dù nó có thể không hiệu quả (xem bình luận của chepner ). Bất kể, Pylint kiểm tra mã để tuân thủ hướng dẫn kiểu PEP 8 , trong đó nêu rõ rằng

Đối với các chuỗi, (chuỗi, danh sách, bộ dữ liệu), sử dụng thực tế là các chuỗi trống là sai.

Yes: if not seq:
     if seq:

No:  if len(seq):
     if not len(seq):

Là một lập trình viên Python thỉnh thoảng, người bỏ qua giữa các ngôn ngữ, tôi coi len(SEQUENCE)cấu trúc này dễ đọc và rõ ràng hơn (Tiết lộ rõ ​​ràng là tốt hơn sau đó ẩn ý). Tuy nhiên, bằng cách sử dụng thực tế là một chuỗi trống đánh giá Falsetrong bối cảnh Boolean được coi là nhiều hơn nữa.


Làm thế nào để thực hiện công việc này sau đó:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
Marichyasana

@Marichyasana Tôi đoán những thứ như thế có thể (về mặt lý thuyết) có thể được viết là if next(iter(...), None) is not None:(nếu chuỗi không thể chứa None). Cái đó dài, nhưng cái đó len(fnmatch...)cũng dài; cả hai cần phải được chia.
Kirill Bulygin

13
Tôi cũng là một người dùng Python thỉnh thoảng và tôi thường có cảm tưởng rằng "cách Pythonic" bị rối trong sự mơ hồ của chính nó.
luqo33

3
Chỉ cần một câu hỏi chung, những khuyến nghị PEP này có thể được sửa đổi? Một lý do khác tại sao theo len(s) == 0ý kiến ​​của tôi là vượt trội là nó có thể khái quát cho các loại trình tự khác. Ví dụ, pandas.Seriesvà mảng numpy. if not s:mặt khác không phải là như vậy, và trong trường hợp đó, bạn sẽ cần sử dụng một đánh giá riêng cho tất cả các loại đối tượng giống như mảng (ví dụ pd.DataFrame.empty).
Sao Hỏa

2
Nhân tiện, không có of collections.abclớp nào nêu __bool__phương thức. Nói cách khác, làm thế nào tôi có thể chắc chắn rằng tôi có thể sử dụng bool(seq)nếu tôi biết rằng đó là một collections.abc.Collection? Moreso, một số thư viện tuyên bố rằng không được phép kiểm tra bool(collection)các lớp học của họ.
Eir Nym

42

Lưu ý rằng việc sử dụng len (seq) trên thực tế là bắt buộc (thay vì chỉ kiểm tra giá trị bool của seq) khi sử dụng mảng NumPy.

a = numpy.array(range(10))
if a:
    print "a is not empty"

dẫn đến một ngoại lệ: ValueError: Giá trị thật của một mảng có nhiều hơn một phần tử là không rõ ràng. Sử dụng a.any () hoặc a.all ()

Và do đó đối với mã sử dụng cả danh sách Python và mảng NumPy, thông báo C1801 ít hữu ích hơn.


5
Tôi đồng ý với lời tuyên bố của bạn. Với vấn đề # 1405 hiện được nêu ra, tôi hy vọng sẽ thấy C1801 được cải tổ thành thứ gì đó hữu ích hoặc bị tắt theo mặc định.
E_net4 là một downvote

2
cộng với nó là vô ích để kiểm tra nếu một chuỗi có một số phần tử nhất định. Nó chỉ tốt để kiểm tra nó là hoàn toàn trống rỗng trong trường hợp tốt nhất.
PabTorre

1

Đây là một vấn đề trong môn vị, và nó không còn được xem xét len(x) == 0 là không chính xác.

Bạn không nên sử dụng trần len(x) như một điều kiện. So sánh len(x)với một giá trị rõ ràng, chẳng hạn như if len(x) == 0củaif len(x) > 0 là hoàn toàn tốt đẹp và không bị cấm bởi PEP 8.

Từ PEP 8 :

# Correct:
if not seq:
if seq:

# Wrong:
if len(seq):
if not len(seq):

Lưu ý rằng việc kiểm tra rõ ràng về độ dài không bị cấm. Các Zen của Python khẳng định:

Rõ ràng là tốt hơn so với ngầm.

Trong sự lựa chọn giữa if not seqif not len(seq), cả hai đều ngầm nhưng hành vi là khác nhau. Nhưng if len(seq) == 0hayif len(seq) > 0 là so sánh rõ ràng và trong nhiều bối cảnh hành vi chính xác.

Trong pylint, PR 2815 đã sửa lỗi này, lần đầu tiên được báo cáo là vấn đề 2684 . Nó sẽ tiếp tục phàn nàn về if len(seq), nhưng nó sẽ không còn phàn nàn về if len(seq) > 0. Bản PR đã được hợp nhất 2019-03-19 vì vậy nếu bạn đang sử dụng pylint 2.4 (phát hành 2019-09-14), bạn sẽ không thấy vấn đề này.


0

Pylint đã thất bại cho mã của tôi và nghiên cứu đã dẫn tôi đến bài viết này:

../filename.py:49:11: C1801: Do not use `len(SEQUENCE)` to determine if a sequence is empty (len-as-condition)
../filename.py:49:34: C1801: Do not use `len(SEQUENCE)` to determine if a sequence is empty (len-as-condition)

Đây là mã của tôi trước đây:

def list_empty_folders(directory):
"""The Module Has Been Build to list empty Mac Folders."""
for (fullpath, dirnames, filenames) in os.walk(directory):
    if len(dirnames) == 0 and len(filenames) == 0:
        print("Exists: {} : Absolute Path: {}".format(
            os.path.exists(fullpath), os.path.abspath(fullpath)))

Đây là sau khi sửa mã của tôi. Bằng cách sử dụng int() attribute, tôi dường như đã thỏa mãn Pep8 / Pylint và dường như không có tác động tiêu cực đến mã của tôi:

def list_empty_folders(directory):
"""The Module Has Been Build to list empty Mac Folders."""
for (fullpath, dirnames, filenames) in os.walk(directory):
    if len(dirnames).__trunc__() == 0 and len(filenames).__trunc__() == 0:
        print("Exists: {} : Absolute Path: {}".format(
            os.path.exists(fullpath), os.path.abspath(fullpath)))

Sửa chữa của tôi

Bằng cách thêm .__trunc__()vào chuỗi, nó dường như đã giải quyết nhu cầu.

Tôi không thấy sự khác biệt trong hành vi, nhưng nếu có ai biết chi tiết cụ thể mà tôi đang thiếu, xin vui lòng cho tôi biết.


1
Bạn đang gọi __trunc__()đầu ra của len(seq), cái mà (hơi dư thừa) cắt ngắn giá trị độ dài thành một số nguyên. Nó chỉ "làm mờ" xơ vải mà không giải quyết được lý do đằng sau nó. Không phải đề xuất trong câu trả lời được chấp nhận làm việc cho bạn?
E_net4 là một downvote

Không phải trong nỗ lực của tôi. Tôi hiểu sự dư thừa, nhưng ngay cả sau khi vấn đề này đã được các nhà phát triển giải quyết trong github.com/PyCQA/pylint/issues/1405 & 2684 và đã được hợp nhất, theo tôi hiểu thì đây không phải là vấn đề khi chạy pylint nhưng Tôi vẫn thấy vấn đề này ngay cả sau khi cập nhật pylint của tôi. Tôi chỉ muốn chia sẻ this worked for me, ngay cả khi nó không hoàn toàn phù hợp. Nhưng, để làm rõ ngay cả khi nó là dư thừa nếu bạn đang thực hiện so sánh len (seq) == 0, trunc không cần phải làm bất cứ điều gì vì chúng đã là số nguyên. đúng?
JayRizzo

1
Chính xác, nó đã là một số nguyên và __trunc__()không làm gì có ý nghĩa. Lưu ý rằng tôi không coi việc so sánh là dư thừa, nhưng với nỗ lực này trong việc cắt ngắn độ dài. Cảnh báo chỉ biến mất vì nó chỉ mong đợi một biểu thức của biểu mẫu len(seq) == 0. Tôi tin rằng lint trong trường hợp này sẽ mong bạn thay thế câu lệnh if bằng câu sau:if not dirnames and not filenames:
E_net4 là một downvote

Việc kiểm tra tính trung thực có những hậu quả không lường trước là "luôn luôn đúng" nếu __bool__hàm không được xác định trong chuỗi bên dưới.
Erik Aronesty
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.