Saltar al contenido principal
Página

Tema 4.2 – Recomendaciones de programación

  • El código debe estar escrito de manera que no perjudique a otras implementaciones de Python (PyPy, Jython, IronPython, Cython, Psyco y demás).
  • Las comparaciones con None siempre deben hacerse con is o is not, nunca con los operadores de igualdad.
  • Use el operador is not en lugar de not ... is. Si bien ambas expresiones son funcionalmente idénticas, la primera es más legible y preferida:
1
2
# Correcto:
if foo is not None:

1
2
# Incorrecto:
if not foo is None:

  • Al implementar operaciones de ordenamiento con comparaciones, es mejor implementar las seis operaciones ( __eq__ , __ne__ , __lt__ , __le__ , __gt__ , __ge__ ) en lugar de depender de otro código para ejercer solo una comparación particular.

Para minimizar el esfuerzo involucrado, el decorador functools.total_ordering() proporciona una herramienta para generar los métodos de comparación que faltan.

PEP 207 indica que las reglas de reflexividad son asumidas por Python. Por lo tanto, el intérprete puede intercambiar y > x con x < y, y > = x con x  <= y, y puede intercambiar los argumentos de x == y y x! = y. Las operaciones sort() y min() garantizan usar el operador < y la función max() usa el operador >. Sin embargo, es mejor implementar las seis operaciones para que no surja confusión en otros contextos.

  • Utilice siempre una sentencia def en lugar de una sentencia de asignación que vincule una expresión lambda directamente a un identificador:
1
2
# Correcto:
def f(x): return 2*x

1
2
# Incorrecto:
f = lambda x: 2*x


La primera manera indica que el nombre del objeto de función resultante es específicamente 'f' en lugar del genérico '<lambda>'. Esto es más útil para trazar el comportamiento de la función y representaciones en cadenas en general. El uso de la asignación por sentencia elimina el único beneficio que puede ofrecer una expresión lambda ante una sentencia def (por ejemplo, que puede integrarse dentro de una expresión más grande).

  • Derive excepciones desde Exception en lugar de BaseException. La herencia directa de BaseException es reservada para excepciones en donde capturarlas es casi siempre el método incorrecto de hacerlo.

Diseñe las jerarquías de excepciones basadas en las distinciones donde las excepciones sean necesarias, y no en lugar desde donde se lanzan. Intente responder la pregunta "¿Qué salió mal?" mediante programación, en lugar de solo indicar que "se produjo un problema".

Las convenciones de nombramiento de clases se aplican aquí, aunque debe agregar el sufijo "Error" a sus clases de excepción si la excepción es un error. Las excepciones que no correspondan a un error, deben ser utilizadas para un flujo de control no local u otro tipo de señalización, estas no necesitan un sufijo en especial.

  • Use la cadena de excepción apropiadamente. En Python 3, "raise X from Y" debe usarse para indicar un reemplazo explícito sin perder el “traceback” original.

Al reemplazar deliberadamente una excepción interna ("raise X from None" en Python 3.3+), asegúrese de que los detalles relevantes se transfieran a la nueva excepción (como preservar el nombre del atributo al convertir KeyError a AttributeError, o añadiendo el texto de la excepción original en el nuevo mensaje de excepción).

  • Cuando este capturando excepciones, mencione sus nombres específicamente siempre que sea posible, en lugar de usar simplemente la cláusula except : .

1
2
3
4
try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None


Una simple cláusula except : capturará excepciones SystemExit y KeyboardInterrupt, lo que dificultará la interrupción de un programa con Control-C, y puede causar otros problemas. Si desea capturar todas las excepciones que señalan errores del programa, use except Exception: (El except es equivalente a except BaseException:).

Una buena regla general es limitar el uso de la simple cláusula 'except' a dos casos:


1.      Si el manejador de excepciones se imprimirá o registrará la excepción; lo que significa que el usuario sabrá que se ha producido un error.

2.      Si el código necesita hacer un trabajo de limpieza, pero luego deja que la excepción se propague hacia arriba con raise. try...finally puede ser una mejor manera para manejar este caso.

  • Al detectar errores del sistema operativo, use la jerarquía de excepción explícita introducida en Python 3.3 en lugar de la introspección de valores errno.
  • Para todas las cláusulas try / except, limite la cláusula try a una cantidad mínima absoluta de líneas de código necesaria. De nuevo, esto evita errores:
1
2
3
4
5
6
7
# Correcto:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

1
2
3
4
5
6
7
# Incorrecto:
try:
    # ¡Muy extenso!
    return handle_value(collection[key])
except KeyError:
    # También capturará la excepción KeyError lanzada por handle_value()
    return key_not_found(key)

  • Cuando un recurso es local para una sección de código particular, use la sentencia with para asegurarse de que se limpia de manera rápida y confiable después de su uso. Una declaración try / finally también es aceptable.
  • Los manejadores de contexto (context managers) deben ser invocados a través de funciones o métodos separados, siempre y cuando que hagan algo diferente a adquirir y liberar recursos:
1
2
3
# Correcto:
with conn.begin_transaction():
    do_stuff_in_transaction(conn)

1
2
3
# Incorrecto:
with conn:
    do_stuff_in_transaction(conn)

El último ejemplo no proporciona ninguna información para indicar que los métodos __enter__ y __exit__ están haciendo algo más que cerrar la conexión después de una transacción. Ser explícito es importante en este caso.

  • Sea consistente en las declaraciones de retorno. Todas las declaraciones de retorno en una función deberían devolver una expresión, o si no se retorna nada ninguna de ellas debería hacerlo. Si cualquier declaración de retorno no devuelve ningún valor, debe indicarlo explícitamente como return None, y una declaración de retorno explícita debe estar presente al final de la función (si es accesible):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Correcto:

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
if x < 0:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
return None
return math.sqrt(x)
# Incorrecto:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

  • Use métodos de cadena en lugar del módulo de cadena.

Los métodos de cadena siempre son mucho más rápidos y comparten la misma API con cadenas unicode. No use esta regla si se requiere compatibilidad con versiones anteriores a Python 2.0.

  •  Use ' '.startswith() y ' '.endswith() en lugar de segmentación de cadenas para buscar prefijos o sufijos.

startswith() y endswith() son más limpios y menos propensos a errores:

1
2
# Correct:
if foo.startswith('bar'):

1
2
# Wrong:
if foo[:3] == 'bar':

  • Las comparaciones de tipos de objeto siempre deben usar isinstance() en lugar de comparar tipos directamente:

1
2
# Correcto:
if isinstance(obj, int):

1
2
# Incorrecto:
if type(obj) is type(1):

  • Para secuencias (strings, listas, tuplas), use el hecho de que sí estas secuencias se encuentran vacías son falsas:

1
2
3
# Correcto:
if not seq:
if seq:

1
2
3
# Incorrecto:
if len(seq):
if not len(seq):

  • No escriba cadenas literales con un espacio grande al final. Dicho espacio en blanco al final no se puede identificar visualmente y algunos editores (o los más recientes, reindent.py) los recortarán.
  •  No compare los valores booleanos con Verdadero o Falso usando == :
1
2
# Correcto:
if greeting:

1
2
# Incorrecto:
if greeting == True:


Y peor aún:

1
2
# Incorrecto:
if greeting is True:

Anotación de variables

  • Las anotaciones para variables a nivel de módulo, las variables de clase e instancia y las variables locales deben tener un solo espacio después del carácter de dos puntos (“: ”).

  • No debe haber espacio antes de los dos puntos.

  • Si una tarea tiene un lado derecho, entonces el signo igual (“=”) debe tener exactamente un espacio en ambos lados:

1
2
3
4
5
6
7
# Correcto:

code: int

class Point:
    coords: Tuple[int, int]
    label: str = '<unknown>'

1
2
3
4
5
6
7
# Incorrecto:

code:int  # Sin espacios después de los dos puntos
code : int  # Espacio antes de los dos puntos

class Test:
    result: int=0  # No hay espacios antes y después del signo igual



Última modificación: jueves, 16 de julio de 2020, 23:05