Perché l'uso di Len (SEQUENZA) in valori di condizione considerati errati da Pylint?


211

Considerando questo frammento di codice:

from os import walk

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

Sono stato allarmato da Pylint con questo messaggio per quanto riguarda la riga con l'istruzione if:

[pylint] C1801: non utilizzare len(SEQUENCE)come valore di condizione

La regola C1801, a prima vista, non mi sembrava molto ragionevole e la definizione sulla guida di riferimento non spiega perché questo sia un problema. In effetti, lo chiama decisamente un uso errato .

len-as-condition (C1801) : non utilizzare len(SEQUENCE)come valore condizione Utilizzato quando Pylint rileva un uso errato di len (sequenza) all'interno delle condizioni.

Anche i miei tentativi di ricerca non sono riusciti a fornirmi una spiegazione più approfondita. Comprendo che la proprietà della lunghezza di una sequenza può essere valutata pigramente e che __len__può essere programmata per avere effetti collaterali, ma è discutibile se questo da solo sia abbastanza problematico per Pylint da chiamare un tale uso errato. Quindi, prima di configurare semplicemente il mio progetto per ignorare la regola, vorrei sapere se mi manca qualcosa nel mio ragionamento.

Quando l'uso di len(SEQ)come condizione è problematico? Quali sono le principali situazioni che Pylint sta tentando di evitare con C1801?


9
Perché puoi valutare direttamente la veridicità della sequenza. pylint vuole che tu faccia if files:oif not files:
Patrick Haugh,

38
lennon conosce il contesto in cui viene chiamato, quindi se calcolare la lunghezza significa attraversare l'intera sequenza, deve; non sa che il risultato è stato appena confrontato con 0. Il calcolo del valore booleano può arrestarsi dopo aver visto il primo elemento, indipendentemente dalla durata effettiva della sequenza. Penso che il pilastro sia un po 'supponente qui; Non riesco a pensare a nessuna situazione in cui è sbagliato usare len, solo che è un'opzione peggiore rispetto all'alternativa.
Chepner,

2
@ E_net4 Penso che PEP-8 sia probabilmente il punto di partenza.
Patrick Haugh,


6
Le SEQUENZE richiedono un 'vuoto ()' o 'isempty ()' come C ++ imo.
JDonner,

Risposte:


281

Quando l'uso di len(SEQ)come condizione è problematico? Quali sono le principali situazioni che Pylint sta tentando di evitare con C1801?

Non è davvero problematico da usare len(SEQUENCE), anche se potrebbe non essere così efficiente (vedi il commento di Chepner ). Indipendentemente da ciò, Pylint verifica la conformità del codice con la guida di stile PEP 8 che lo afferma

Per le sequenze (stringhe, elenchi, tuple), usa il fatto che le sequenze vuote sono false.

Yes: if not seq:
     if seq:

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

Come programmatore Python occasionale, che scorre tra le lingue, considererei il len(SEQUENCE)costrutto più leggibile ed esplicito ("Explicit è meglio che implicito"). Tuttavia, l'utilizzo del fatto che una sequenza vuota viene valutata Falsein un contesto booleano è considerato più "pitonico".


Come farlo funzionare allora:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
Marichyasana,

@Marichyasana Immagino che cose del genere possano (teoricamente) essere scritte come if next(iter(...), None) is not None:(se la sequenza non può contenere None). È lungo, ma len(fnmatch...)è anche lungo; entrambi devono essere divisi.
Kirill Bulygin,

13
Sono anche un utente occasionale di Python e spesso ho l'impressione che il "modo Pythonic" si sia in qualche modo aggrovigliato nella sua stessa ambiguità.
luqo33,

3
Solo una domanda generale, queste raccomandazioni PEP possono essere riviste? Un altro motivo per cui len(s) == 0secondo me è superiore è generalizzabile per altri tipi di sequenze. Ad esempio, pandas.Seriesmatrici intorpidite. if not s:non è d'altra parte, e in quel caso dovresti usare una valutazione separata per tutti i possibili tipi di oggetti simili ad array (es pd.DataFrame.empty.).
Marses,

2
A proposito, nessuna of collections.abcclasse indica il __bool__metodo. In altre parole, come posso essere sicuro di poter usare bool(seq)se so che è un collections.abc.Collection? Moreso, alcune biblioteche dichiarano che è vietato controllare le bool(collection)loro classi.
Eir Nym,

42

Si noti che l'uso di len (seq) è in effetti richiesto (invece di controllare solo il valore bool di seq) quando si usano gli array NumPy.

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

genera un'eccezione: ValueError: il valore di verità di un array con più di un elemento è ambiguo. Usa a.any () o a.all ()

E quindi per il codice che utilizza sia gli elenchi Python sia gli array NumPy, il messaggio C1801 è poco utile.


5
Sono d'accordo con la tua dichiarazione. Con il numero 1405 ora sollevato, spero di vedere C1801 o riformato in qualcosa di utile o disabilitato di default.
E_net4 della brigata di downvote

2
inoltre è inutile verificare se una sequenza ha un determinato numero di elementi. Va bene solo per verificarlo, nel migliore dei casi è completamente vuoto.
PabTorre,

1

Questo era un problema in Pylint e non è più considerato len(x) == 0errato.

Non dovresti usare un nudo len(x) come condizione. Confrontando len(x)contro un valore esplicito, come if len(x) == 0di if len(x) > 0è assolutamente soddisfacente e non vietato dalla PEP 8.

Da PEP 8 :

# Correct:
if not seq:
if seq:

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

Si noti che il test esplicito per la lunghezza non è proibito. Lo Zen di Python afferma:

Esplicito è meglio che implicito.

Nella scelta tra if not seqe if not len(seq), entrambi sono impliciti ma il comportamento è diverso. Ma if len(seq) == 0o if len(seq) > 0sono confronti espliciti e in molti contesti il ​​comportamento corretto.

In pylint, PR 2815 ha corretto questo bug, segnalato per la prima volta come numero 2684 . Continuerà a lamentarsi if len(seq), ma non si lamenterà più if len(seq) > 0. Il PR è stato unito il 19-03-2019, quindi se si utilizza il pilastro 2.4 (rilasciato il 14.09.2019) non si dovrebbe vedere questo problema.


0

Pylint stava fallendo il mio codice e la ricerca mi ha portato a questo post:

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

Questo era il mio codice prima:

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

Questo è stato dopo la mia correzione del codice. Usando il int() attribute, mi sembra di aver soddisfatto Pep8 / Pylint e non sembra avere un impatto negativo sul mio codice:

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

La mia correzione

Aggiungendo .__trunc__()alla sequenza sembra aver risolto il bisogno.

Non vedo differenze nel comportamento, ma se qualcuno conosce i dettagli che mi mancano, per favore fatemelo sapere.


1
Stai chiamando __trunc__()l'output di len(seq), che (in qualche modo ridondante) tronca il valore di lunghezza a un numero intero. "Finge" solo la lanugine senza affrontare la ragione. Il suggerimento nella risposta accettata non ha funzionato per te?
E_net4 della brigata di downvote il

Non nei miei tentativi. Capisco la ridondanza, ma anche dopo che questo problema è stato risolto dagli sviluppatori in github.com/PyCQA/pylint/issues/1405 e 2684 ed è stato unito, a quanto ho capito, questo non dovrebbe essere un problema quando si esegue Pylint ma Riscontro ancora questo problema anche dopo aver aggiornato il mio pilastro. Volevo solo condividere, come this worked for me, anche se non è del tutto appropriato. Ma, per chiarire anche se è ridondante se si sta eseguendo un confronto len (seq) == 0, trunc non dovrebbe fare nulla in quanto sono già numeri interi. giusto?
JayRizzo,

1
Esatto, è già un numero intero e __trunc__()non fa nulla di significativo. Nota che non ho fatto riferimento al confronto come ridondante, ma a questo tentativo di troncare la lunghezza. L'avviso scompare solo perché si aspetta solo un'espressione del modulo len(seq) == 0. Credo che il filone in questo caso si aspetterebbe che tu sostituisca la dichiarazione if con la seguente:if not dirnames and not filenames:
E_net4 della brigata di downvote

Il test per la verità ha le conseguenze non intenzionali di essere "sempre vero" se la __bool__funzione non è definita nella sequenza sottostante.
Erik Aronesty,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.