Cuando Python se traiciona a sí mismo
No siempre se respeta el Zen de Python
Si programas o has programado en serio en Python, seguro te topaste con el “Zen de Python”.
Si no lo conoces, puedes abrir la consola, ejecutar Python y en el REPL escribes import this
y obtendrás el Zen de Python de este modo:
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
La idea de estas máximas es establecer ciertos principios para este lenguaje de programación. Lo que me parece muy bien.
Hay una sentencia que es citada muchas veces y es la siguiente:
There should be one-- and preferably only one --obvious way to do it.
O sea, debería haber una única manera obvia de hacer algo en Python.
OK, pero que pasa si les pido formatear un string. Más en concreto, supongamos que les pido implementar la función hello(name: str) → str
, que reciba un nombre y retorne un string de “saludo”. Por ejemplo, si ejecuto hello(“Eduardo”)
me retorne “Hello, Eduardo”
.
Muy bien, seguro que se te ocurrió algo como esto:
def hello(name: str) -> str:
return 'Hello, '+name
Pero hay otra forma, que vamos a llamar el formateo clásico:
def hello(name: str) -> str:
return 'Hello, %s' % name
Que es la forma que le gusta mucho a los que vienen de C.
Pero está la forma nueva, introducida en Python 3:
def hello(name: str) -> str:
return 'Hello, {}'.format(name)
Pero en Python 3.6 se introdujo la interpolación de literales dentro de un string, un nombre muy bonito para esto:
def hello(name: str) -> str:
return f'Hello, {name}'
OK, sin embargo, hay más maneras, como por ejemplo usar “template strings”, del siguiente modo:
from string import Template
def hello(name: str) -> str:
t = Template('Hello, $name')
return t.substitute(name=name)
Y en este punto me dirás: ¡por qué te complicas tanto!
Y yo te voy a responder que este método es muy útil si recibes la entrada de un usuario y quieres evitar problemas de seguridad, porque template “sanitiza” la entrada.
Así que sí, no siempre en Python hay un modo evidente y único de hacer las cosas.
Acá te dejo una regla que puedes aplicar la próxima vez que tengas que formatear strings en Python.
Si los strings usados como plantilla de formato son entregadas por el usuario, entonces usa Template, para evitar problemas de seguridad. Sino usa interpolación de strings. Si por alguna razón estás usando una versión anterior a 3.6 entonces usa .format.
Ahora es tu turno, ¿conoces otros ejemplos en que hay más de una manera de hacer algo en Python? ¿Por qué crees que pasa esto?
Al usar logs con la biblioteca incluida en python logging: podrías teóricamente formatear strings de cualquier manera.
Pero la buena práctica del uso de esta librería es la siguiente, muy similar, más no igual, al formateo "clásico":
digamos que una variable a loggear es
lnds="La Naturaleza del Software"
Entonces se debe loggear con logging así:
logger.info("Este log lo verán en '%s'", lnds)
Para que, dependiendo el formatter usado, aparezca algo así:
2024-10-17 08:30:23 [INFO] - Este log lo verán en 'La Naturaleza del Software '
Qué lo hace diferente? Y por qué es buena práctica?
En el clásico se formatea así:
"un string: %s" % "hola"
Es decir formateas usando % como placeholder y % como operador para "ejecutar" el formateo.
En logging usas % como placeholder y no mandas el operador %, solo lo que quieres sustituir como argumentos de la función de log que uses.
Por qué es buena práctica? Porque dependiendo el nivel de logging que desees, podría ser que ni siquiera ese log se vaya a pintar. Suponiendo que usas logger.debug a lo mejor nunca los ves. O tienes el nivel en ERROR, los INFO ni siquiera se pintarán .
Al usar ese "formateo", propio de logging, las variables ni se meterán al intérprete de python.
Usando otro mecanismo, el parseo de strings de python obliga a python a meter el procesamiento en el string y gastar cómputo en algo que no terminará pintando
Quizá sea algo irrelevante ahorrarse cómputo en eso, pero a veces si es crítico.