DEV Community

Baltasar García Perez-Schofield
Baltasar García Perez-Schofield

Posted on • Edited on

¿Y si Python permitiera 'end' como fin de bloque? II

Hace unos días, pensé en una propuesta que, según yo mismo, mejoraría la legibilidad del código fuente en Python.

Al final, examiné las normas de propuestas de cambios para Python. Lo primero que se me vino a la cabeza fueron los PEP (Propuestas de mejora de Python o Python Enhance Proposals), pero el paso previo era discutir la cuestión primero en discuss.python.org, sección ideas.

Dicho y hecho, propuse la idea en el foro, y, como quien dice, me senté a esperar los resultados.

Recordemos el ejemplo de código.

i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)

    i += 1
Enter fullscreen mode Exit fullscreen mode

Al no haber marcas de fin de bloque más allá de la indentación, se pierde legibilidad. Por ejemplo, en este código la instrucción i += 1 parece como que "cuelga" por su cuenta.

A ver, lo cierto es solo tenía una vaga esperanza de que la idea tuviera tracción (los pythonistas son muy suyos), y así fue, la mayor parte de las respuestas son más del tipo troll que otra cosa.

Pero sí hubo algunas propuestas interesantes. ¡Analicémoslas!

  • Utilizar # end. En mi ejemplo, proponía el uso de una palabra clave end, diciendo que en este momento no se soportaba a no ser que se transformase en un comentario. Una propuesta es hacer justo eso:
i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)
    # end

    i += 1
# end
Enter fullscreen mode Exit fullscreen mode

No hay compromisos, es decir, adoptar este estilo de código fuente no afectaría en nada al compilador (ni siquiera habría que modificarlo), ni al código generado (que no cambia en nada). El nuevo estilo es optativo, evidentemente, pues de todas formas no podríamos forzar un nuevo estilo en un lenguaje de programación tan maduro como Python.

  • Utilizar ... o pass en el lugar de end. Todos conocemos pass como una palabra clave que permite crear un bloque de código sin contenido real. Una nueva versión de pass es ... (ambas son versiones de la clase Elipsis). Podemos utilizarlos en el lugar del end deseado.
i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)
    ...

    i += 1
...
Enter fullscreen mode Exit fullscreen mode

Lo cierto es que funciona sin problema, al igual que la propuesta anterior del comentario, pero no es muy idiomático.

  • Utilizar una constante end como una expresión libre, es decir, sin uso. Esta propuesta quedaría así:
end = "end"

i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)
    end

    i += 1
end
Enter fullscreen mode Exit fullscreen mode

Esta solución es ingeniosa, y aparentemente consigue solucionar todos los posibles problemas vistos hasta ahora. El problema... el problema es que genera código, a pesar de que la expresión no se utiliza en absoluto. Esto podemos comprobarlo con dis, el módulo desensamblador de la librería estándar de Python.

import dis

dis.dis("""
end = "end"

i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)
    end

    i += 1
end
""")
<...>
                LOAD_GLOBAL              4 (end)
             96 POP_TOP
             98 JUMP_BACKWARD           34 (to 32)
Enter fullscreen mode Exit fullscreen mode

Python utiliza una máquina virtual basada en una pila. Por ejemplo, para sumar dos números a y b metería (push, o load), primero a, después b, y después ejecutaría el operador +, que tomaría sus dos parámetros sacándolos (pop) de la pila. En el ejemplo de arriba, vemos como mete en la pila end, para inmediatamente ejecutar POP_TOP, es decir, eliminar el tope de la pila. Aunque el código a ejecutar es mínimo y probablemente resulte eliminado por la máquina virtual durante la ejecución, no tiene mucho sentido que un mecanismo de estilo modifique el código a ejecutar.

  • Mezclar las dos propuestas anteriores. Es decir, ya que es posible crear constantes como end, en lugar de hacerla valer una cadena de caracteres sin interés, hacerla valer directamente la elipsis. Esto haría ver al compilador, al contener la constante una elipsis, que no requiere generar código para ella.
end = ...

i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)
    end

    i += 1
end
Enter fullscreen mode Exit fullscreen mode

El problema es que... sucede exactamente lo mismo que en la versión anterior. Es decir, a pesar de que la constante end alberga la elipsis, el compilador no es capaz de darse cuenta de que no debería generar código, primero porque va a generar un LOAD inmediatamente seguido de un POP_TOP, y segundo porque el contenido de la constante es la elipsis, que ahí sí ya es seguro que no va a generar nada útil.

Así que, finalmente, las dos posibilidades más realistas son utilizar # end o .... Todas las posibilidades basadas en una constante funcionan, pero modifican el código generado, algo que no deseamos. La mejor opción sería crear no una constante (que al fin y al cabo, no deja de ser en realidad una variable global), sino algún tipo de alias o de macrosustitución, algo que no es soportado en Python.

¿Alguna idea? ¡Pónmelo en los comentarios!

Top comments (0)