Dlaczego użycie len (SEKWENCJA) w wartościach warunków jest uważane przez Pylint za nieprawidłowe?

211

Biorąc pod uwagę ten fragment kodu:

from os import walk

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

Pylint zaniepokoił mnie tą wiadomością dotyczącą wiersza z instrukcją if:

[pylint] C1801: Nie należy używać len(SEQUENCE)jako wartości warunku

Zasada C1801 na pierwszy rzut oka nie brzmiała dla mnie zbyt rozsądnie, a definicja w podręczniku nie wyjaśnia, dlaczego jest to problem. W rzeczywistości wręcz nazywa to niewłaściwym użyciem .

len-as-condition (C1801) : Nie należy używać len(SEQUENCE)jako wartości warunku Używane, gdy Pylint wykryje nieprawidłowe użycie len (sekwencji) w warunkach wewnętrznych.

Moje próby wyszukiwania również nie dały mi głębszego wyjaśnienia. Rozumiem, że właściwość długości sekwencji może być leniwie oceniana i __len__może być zaprogramowana tak, aby wywoływała skutki uboczne, ale wątpliwe jest, czy sama ta jest wystarczająco problematyczna, aby Pylint mógł nazwać takie użycie niepoprawnym. Dlatego zanim skonfiguruję mój projekt tak, aby ignorował regułę, chciałbym wiedzieć, czy coś mi brakuje w moim rozumowaniu.

Kiedy użycie len(SEQ)jako warunku jest problematyczne? Jakie główne sytuacje próbuje uniknąć Pylint z C1801?

E_net4 nie jest słodki
źródło
9
Ponieważ możesz bezpośrednio ocenić prawdziwość sekwencji. pylint chce, żebyś zrobił if files:lubif not files:
Patrick Haugh
38
lennie zna kontekstu, w którym jest wywoływany, więc jeśli obliczenie długości oznacza przejście całej sekwencji, musi; nie wie, że wynik jest właśnie porównywany z 0. Obliczenie wartości logicznej może zostać zatrzymane po zobaczeniu pierwszego elementu, niezależnie od tego, jak długo faktycznie trwa sekwencja. Myślę jednak, że pylint jest tu nieco opiniotwórczy; Nie mogę wymyślić żadnej sytuacji, w której użycie byłoby niewłaściwelen , tylko że jest to gorsza opcja niż alternatywa.
chepner
2
@ E_net4 Myślę, że PEP-8 jest prawdopodobnie miejscem, od którego można zacząć.
Patrick Haugh
6
SEKWENCJE potrzebują „pustego ()” lub „isempty ()”, takiego jak C ++ imo.
JDonner,

Odpowiedzi:

281

Kiedy użycie len(SEQ)jako warunku jest problematyczne? Jakie główne sytuacje próbuje uniknąć Pylint z C1801?

Nie jest to zbyt problematyczne w użyciu len(SEQUENCE)- choć może nie być tak wydajne (patrz komentarz chepnera ). Niezależnie od tego, Pylint sprawdza kod pod kątem zgodności z przewodnikiem stylu PEP 8, który to stwierdza

W przypadku sekwencji (ciągów, list, krotek) użyj faktu, że puste sekwencje są fałszywe.

Yes: if not seq:
     if seq:

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

Jako sporadyczny programista Python, który porusza się między językami, uważam, że len(SEQUENCE)konstrukcja jest bardziej czytelna i wyraźna („Jawna jest lepsza niż niejawna”). Jednak użycie faktu, że pusta sekwencja jest ewaluowana Falsew kontekście logicznym, jest uważane za bardziej „pytoniczne”.

Anthony Geoghegan
źródło
Jak sprawić, by to zadziałało:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
Marichyasana
@Marichyasana Myślę, że takie rzeczy można (teoretycznie) zapisać jako if next(iter(...), None) is not None:(jeśli sekwencja nie może zawierać None). To długo, ale też len(fnmatch...)jest długie; oba muszą być podzielone.
Kirill Bulygin
13
Jestem także sporadycznym użytkownikiem Pythona i często mam wrażenie, że „sposób Pythona” zaplątał się w swoją dwuznaczność.
luqo33
3
Tylko ogólne pytanie, czy te zalecenia PEP mogą zostać zmienione? Innym powodem, dla którego według mnie len(s) == 0jest lepszy, jest to, że można go uogólniać dla innych typów sekwencji. Na przykład pandas.Seriesi tablice numpy. if not s:nie jest z drugiej strony, w takim przypadku należałoby zastosować osobną ocenę dla wszystkich możliwych typów obiektów podobnych do tablic (tj pd.DataFrame.empty.).
Marses
2
Nawiasem mówiąc, żadna of collections.abcklasa nie określa __bool__metody. Innymi słowy, jak mogę się upewnić, że mogę z niego korzystać, bool(seq)jeśli wiem, że to jest collections.abc.Collection? Moreso, niektóre biblioteki deklarują, że zabronione jest sprawdzanie bool(collection)ich klas.
Eir Nym,
42

Zauważ, że użycie len (seq) jest w rzeczywistości wymagane (zamiast po prostu sprawdzania wartości bool seq) podczas korzystania z tablic NumPy.

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

powoduje wyjątek: ValueError: Wartość prawdy tablicy z więcej niż jednym elementem jest niejednoznaczna. Użyj a.any () lub a.all ()

Dlatego w przypadku kodu, który korzysta zarówno z list Pythona, jak i tablic NumPy, komunikat C1801 jest mniej niż pomocny.

Cameron Hayne
źródło
5
Zgadzam się z twoim oświadczeniem. Po podniesieniu problemu # 1405 mam nadzieję, że C1801 zostanie zreformowany do czegoś przydatnego lub domyślnie wyłączony.
E_net4 nie jest słodki
2
a ponadto jest bezużyteczny do sprawdzania, czy sekwencja ma określoną liczbę elementów. Przydaje się tylko do sprawdzania, czy w najlepszych przypadkach jest całkowicie pusty.
PabTorre
1

Był to problem z pylintem i nie jest już uważany len(x) == 0za niepoprawny.

Nie powinieneś używać goły len(x) jako warunek. Porównywanie len(x)z wyraźną wartością, taką jak if len(x) == 0of, if len(x) > 0jest całkowicie w porządku i nie jest zabronione przez PEP 8.

Od PEP 8 :

# Correct:
if not seq:
if seq:

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

Pamiętaj, że jawne testowanie długości nie jest zabronione. Zen Pythona stwierdza:

Jawne jest lepsze niż niejawne.

W wyborze między if not seqi if not len(seq)oba są niejawne, ale zachowanie jest inne. Ale if len(seq) == 0lub if len(seq) > 0są wyraźne porównania i w wielu kontekstach prawidłowe zachowanie.

W pylint, PR 2815 naprawił ten błąd, po raz pierwszy zgłoszony jako problem 2684 . Będzie nadal narzekać if len(seq), ale nie będzie już narzekać if len(seq) > 0. PR został scalony 2019-03-19, więc jeśli używasz pylint 2.4 (wydany 14.09.2019), nie powinieneś widzieć tego problemu.

gerrit
źródło
0

Pylint zawiódł w moim kodzie, a badania doprowadziły mnie do tego postu:

../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)

To był mój kod wcześniej:

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

To było po mojej poprawce kodu. Korzystając z int() attribute, wydaje mi się, że jestem zadowolony z Pep8 / Pylint i nie wydaje się mieć negatywnego wpływu na mój kod:

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

Moja poprawka

Po dodaniu .__trunc__()do sekwencji wydaje się, że zaspokoiła potrzebę.

Nie widzę różnicy w zachowaniu, ale jeśli ktoś zna szczegóły, których mi brakuje, proszę dać mi znać.

JayRizzo
źródło
1
Wywołujesz __trunc__()wyjście len(seq), które (nieco redundantnie) obcina wartość długości do liczby całkowitej. To tylko „zwodzi” kłaczki, nie podając przyczyny. Czy sugestia w zaakceptowanej odpowiedzi nie działała dla Ciebie?
E_net4 nie jest słodki
Nie w moich próbach. Rozumiem nadmiarowość, ale nawet po tym, jak ten problem został rozwiązany przez programistów w github.com/PyCQA/pylint/issues/1405 i 2684 i został włączony, według mnie nie powinno to stanowić problemu podczas uruchamiania pylint, ale Nadal widzę ten problem, nawet po zaktualizowaniu mojego pylinta. Chciałem się tylko podzielić, ponieważ this worked for menawet jeśli nie jest to całkowicie właściwe. Ale, aby wyjaśnić, nawet jeśli jest to zbędne, jeśli robisz porównanie len (seq) == 0, trunc nie powinien nic robić, ponieważ są już liczbami całkowitymi. dobrze?
JayRizzo,
1
Dokładnie, jest już liczbą całkowitą i __trunc__()nie robi nic znaczącego. Zauważ, że nie odnosiłem się do porównania jako zbędnego, ale do tej próby skracania długości. Ostrzeżenie znika tylko dlatego, że oczekuje tylko wyrażenia formy len(seq) == 0. Uważam, że kłaczki w tym przypadku spodziewają się, że if not dirnames and not filenames:
zastąpisz
Testowanie prawdziwości ma niezamierzone konsekwencje bycia „zawsze prawdziwym”, jeśli __bool__funkcja nie jest zdefiniowana w sekwencji leżącej u podstaw.
Erik Aronesty,