¿Por qué Pylint considera incorrecto el uso de len (SECUENCIA) en los valores de condición?

211

Teniendo en cuenta este fragmento de código:

from os import walk

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

Pylint me alarmó con este mensaje con respecto a la línea con la declaración if:

[pylint] C1801: No utilizar len(SEQUENCE)como valor de condición

La regla C1801, a primera vista, no me pareció muy razonable, y la definición en la guía de referencia no explica por qué esto es un problema. De hecho, lo llama francamente un uso incorrecto .

len-as-condition (C1801) : no utilizar len(SEQUENCE)como valor de condición Se utiliza cuando Pylint detecta el uso incorrecto de len (secuencia) dentro de las condiciones.

Mis intentos de búsqueda tampoco han podido proporcionarme una explicación más profunda. Entiendo que la propiedad de longitud de una secuencia puede evaluarse perezosamente, y que __len__se puede programar para que tenga efectos secundarios, pero es cuestionable si eso solo es lo suficientemente problemático como para que Pylint considere incorrecto dicho uso. Por lo tanto, antes de simplemente configurar mi proyecto para ignorar la regla, me gustaría saber si me falta algo en mi razonamiento.

¿Cuándo es len(SEQ)problemático el uso de como valor de condición? ¿Qué situaciones principales intenta evitar Pylint con C1801?

E_net4 de la brigada de votos negativos
fuente
9
Porque puedes evaluar la veracidad de la secuencia directamente. pylint quiere que hagas if files:oif not files:
Patrick Haugh
38
lenno conoce el contexto en el que se llama, por lo que si calcular la longitud significa atravesar toda la secuencia, debe hacerlo; no sabe que el resultado solo se está comparando con 0. Calcular el valor booleano puede detenerse después de ver el primer elemento, independientemente de la duración de la secuencia. Sin embargo, creo que Pylint está siendo un poco obstinado aquí; No puedo pensar en ninguna situación en la que sea incorrecto usarla len, solo que es una opción peor que la alternativa.
chepner
2
@ E_net4 Creo que PEP-8 es probablemente el lugar para comenzar.
Patrick Haugh
66
Las SECUENCIAS necesitan un 'empty ()' o 'isempty ()' como C ++ imo.
JDonner

Respuestas:

281

¿Cuándo es len(SEQ)problemático el uso de como valor de condición? ¿Qué situaciones principales intenta evitar Pylint con C1801?

No es realmente problemático de usar len(SEQUENCE), aunque puede que no sea tan eficiente (vea el comentario de chepner ). Independientemente, Pylint verifica el código para el cumplimiento de la guía de estilo PEP 8 que establece que

Para secuencias (cadenas, listas, tuplas), use el hecho de que las secuencias vacías son falsas.

Yes: if not seq:
     if seq:

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

Como programador ocasional de Python, que pasa de un idioma a otro, consideraría que la len(SEQUENCE)construcción es más legible y explícita ("explícito es mejor que implícito"). Sin embargo, usar el hecho de que una secuencia vacía se evalúa Falseen un contexto booleano se considera más "Pitónico".

Anthony Geoghegan
fuente
Cómo hacer que esto funcione entonces:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
Marichyasana
@Marichyasana Supongo que cosas como esa pueden (teóricamente) escribirse como if next(iter(...), None) is not None:(si la secuencia no puede contener None). Eso es largo, pero también lo len(fnmatch...)es; Ambos necesitan ser divididos.
Kirill Bulygin
13
También soy un usuario ocasional de Python y, a menudo, tengo la impresión de que la "forma pitónica" se enreda en su propia ambigüedad.
luqo33
3
Solo una pregunta general, ¿se pueden revisar estas recomendaciones de PEP? Otra razón por la cual len(s) == 0es superior en mi opinión es que es generalizable para otros tipos de secuencias. Por ejemplo, pandas.Seriesy matrices numpy. if not s:no es, por otro lado, y en ese caso necesitaría usar una evaluación separada para todos los tipos posibles de objetos tipo matriz (es decir pd.DataFrame.empty).
Marses
2
Por cierto, ninguna of collections.abcclase establece el __bool__método. En otras palabras, ¿cómo puedo estar seguro de que puedo usar bool(seq)si sé que es un collections.abc.Collection? Más aún, algunas bibliotecas rechazan que esté prohibido verificar bool(collection)sus clases.
Eir Nym
42

Tenga en cuenta que, de hecho, se requiere el uso de len (seq) (en lugar de simplemente verificar el valor bool de seq) cuando se usan matrices NumPy.

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

da como resultado una excepción: ValueError: el valor de verdad de una matriz con más de un elemento es ambiguo. Utilice a.any () o a.all ()

Y, por lo tanto, para el código que usa listas Python y matrices NumPy, el mensaje C1801 no es útil.

Cameron Hayne
fuente
55
Estoy de acuerdo con tu declaración. Con el problema # 1405 ahora planteado, espero ver que el C1801 sea reformado a algo útil o deshabilitado por defecto.
E_net4 de la brigada de votos negativos
2
Además, no sirve para comprobar si una secuencia tiene un número determinado de elementos. Solo es bueno para comprobar que está completamente vacío en el mejor de los casos.
PabTorre
1

Este fue un problema en pylint, y ya no se considera len(x) == 0incorrecto.

No debe usar un desnudo len(x) como condición. Comparar len(x)con un valor explícito, como if len(x) == 0de, if len(x) > 0está totalmente bien y no está prohibido por PEP 8.

De PEP 8 :

# Correct:
if not seq:
if seq:

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

Tenga en cuenta que no está prohibido probar explícitamente la longitud . El Zen de Python dice:

Explícito es mejor que implícito.

En la elección entre if not seqy if not len(seq), ambos son implícitos pero el comportamiento es diferente. Pero if len(seq) == 0o if len(seq) > 0son comparaciones explícitas y en muchos contextos el comportamiento correcto.

En pylint, PR 2815 ha solucionado este error, primero informado como el problema 2684 . Continuará quejándose if len(seq), pero ya no se quejará if len(seq) > 0. El PR se fusionó el 19/03/2019, por lo que si está utilizando pylint 2.4 (lanzado el 14/09/2019), no debería ver este problema.

gerrit
fuente
0

Pylint estaba fallando en mi código y la investigación me llevó a esta publicación:

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

Este era mi código antes:

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

Esto fue después de la reparación de mi código. Al usar elint() attribute , parece que he satisfecho el Pep8 / Pylint y no parece tener un impacto negativo en mi código:

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

Mi arreglo

Añadiendo .__trunc__() a la secuencia parece haber resuelto la necesidad.

No veo una diferencia en el comportamiento, pero si alguien conoce los detalles que me faltan, hágamelo saber.

JayRizzo
fuente
1
Está llamando __trunc__()a la salida de len(seq), que (algo redundantemente) trunca el valor de longitud a un entero. Solo "finta" la pelusa sin abordar la razón detrás de ella. ¿La sugerencia en la respuesta aceptada no funcionó para usted?
E_net4 de la brigada de votos negativos
No en mis intentos. Entiendo la redundancia, pero incluso después de que los desarrolladores hayan solucionado este problema en github.com/PyCQA/pylint/issues/1405 & 2684 y se haya fusionado, a mi entender, esto no debería ser un problema al ejecutar pylint, pero Todavía veo este problema incluso después de actualizar mi pylint. Solo quería compartir, this worked for meincluso si no es del todo apropiado. Pero, para aclarar incluso si es redundante si está haciendo una comparación len (seq) == 0, trunc no debería tener que hacer nada ya que ya son enteros. ¿Derecha?
JayRizzo
1
Exactamente, ya es un número entero y __trunc__()no hace nada significativo. Tenga en cuenta que no me referí a la comparación como redundante, sino a este intento de truncar la longitud. La advertencia solo desaparece porque solo espera una expresión del formulario len(seq) == 0. Creo que la pelusa en este caso esperaría que reemplazaras la declaración if con lo siguiente:if not dirnames and not filenames:
E_net4 de la brigada de votos negativos el
Las pruebas de veracidad tienen las consecuencias no deseadas de ser "siempre cierto" si la __bool__función no está definida en la secuencia subyacente.
Erik Aronesty