Búsqueda de sitios web

Expresión vs Declaración en Python: ¿Cuál es la diferencia?


Después de trabajar con Python por un tiempo, eventualmente te encontrarás con dos términos aparentemente similares: expresión y declaración. Cuando explora la documentación oficial o explora un hilo relacionado con Python en un foro en línea, puede tener la impresión de que la gente usa estos términos indistintamente. Esto suele ser cierto, pero, de manera bastante confusa, hay casos en los que la distinción entre expresión y declaración se vuelve importante.

Entonces, ¿cuál es la diferencia entre expresiones y declaraciones en Python?

En resumen: las expresiones tienen valores y las declaraciones causan efectos secundarios

Cuando abre el glosario de Python, encontrará las dos definiciones siguientes:

Expresión: un fragmento de sintaxis que se puede evaluar con algún valor. (…) (Fuente)

Declaración : una declaración es parte de una suite (un "bloque" de código). Una declaración es una expresión o una de varias construcciones con una palabra clave, (...) (fuente)

Bueno, eso no es particularmente útil, ¿verdad? Afortunadamente, puede resumir los hechos más importantes sobre expresiones y declaraciones en tan solo tres puntos:

  1. Todas las instrucciones en Python se dividen en la amplia categoría de declaraciones.
  2. Según esta definición, todas las expresiones son también declaraciones, a veces llamadas declaraciones de expresión.
  3. No todas las declaraciones son una expresión.

En un sentido técnico, cada línea o bloque de código es una declaración en Python. Esto incluye expresiones, que representan un tipo especial de declaración. ¿Qué hace que una expresión sea especial? Lo descubrirás ahora.

Expresiones: declaraciones con valores

Esencialmente, puede sustituir todas las expresiones en su código con los valores calculados, que producirían en tiempo de ejecución, sin cambiar el comportamiento general de su programa. Las declaraciones, por otro lado, no pueden reemplazarse con valores equivalentes a menos que sean expresiones.

Considere el siguiente fragmento de código:

>>> x = 42
>>> y = x + 8
>>> print(y)
50

En este ejemplo, las tres líneas de código contienen declaraciones. Las dos primeras son declaraciones de asignación, mientras que la tercera es una llamada a la función print() .

Cuando mira cada línea más de cerca, puede comenzar a desmontar la declaración correspondiente en subcomponentes. Por ejemplo, el operador de asignación (=) consiste en las piezas de la izquierda y la derecha. La parte a la izquierda del signo igual indica el nombre de la variable, como x o y , y la parte de la derecha es el valor asignado a esa variable.

La palabra valor es la clave aquí. Observe que a la variable x se le asigna un valor literal, 42, que está integrado directamente en su código. Por el contrario, la siguiente línea asigna una expresión aritmética, x + 8, a la variable y. Python primero debe calcular o evaluar dicha expresión para determinar el valor final de la variable cuando se ejecuta su programa.

Las expresiones aritméticas son sólo un ejemplo de expresiones de Python. Otros incluyen expresiones lógicas, expresiones condicionales y más. Lo que todos tienen en común es un valor que evalúan, aunque cada valor generalmente será diferente. Como resultado, puedes sustituir de forma segura cualquier expresión con el valor correspondiente:

>>> x = 42
>>> y = 50
>>> print(y)
50

Este breve programa ofrece el mismo resultado que antes y es funcionalmente idéntico al anterior. Has calculado la expresión aritmética a mano e ha insertado el valor resultante en su lugar.

Tenga en cuenta que puede evaluar x + 8 , pero no puede hacer lo mismo con la asignación y=x + 8 , aunque incorpora una expresión. Toda la línea de código representa una declaración pura sin valor intrínseco. Entonces, ¿cuál es el punto de tener tales declaraciones? Es hora de sumergirse en las declaraciones de Python y averiguarlo.

Declaraciones: Instrucciones con efectos secundarios

Las declaraciones que no son expresiones causan efectos secundarios, que cambian el estado de su programa o afectan un recurso externo, como un archivo en el disco. Por ejemplo, cuando asigna un valor a una variable, define o redefine esa variable en algún lugar de la memoria de Python. Del mismo modo, cuando llama a print() , escribe efectivamente en la transmisión de salida estándar (stDout), que, por defecto, muestra texto en la pantalla.

Bueno. Has cubierto declaraciones que son expresiones y declaraciones que no son expresiones. De ahora en adelante, puede referirse a ellos como expresiones puros y declaraciones puras , respectivamente. Pero resulta que hay un punto medio aquí.

Algunas instrucciones pueden tener valor y causar efectos secundarios al mismo tiempo. En otras palabras, son expresiones con efectos secundarios o, equivalentemente, afirmaciones con un valor. Un excelente ejemplo de esto sería la función next() de Python integrada en el lenguaje:

>>> fruit = iter(["apple", "banana", "orange"])
>>> next(fruit)
'apple'
>>> next(fruit)
'banana'
>>> next(fruit)
'orange'

Aquí, define un objeto iterador llamado fruit, que le permite iterar sobre una lista de nombres de frutas. Cada vez que llamas a next() en este objeto, modificas su estado interno moviendo el iterador al siguiente elemento de la lista. Ese es tu efecto secundario. Simultáneamente, next() devuelve el nombre de la fruta correspondiente, que es la parte del valor de la ecuación.

En general, se considera una buena práctica no mezclar valores con efectos secundarios en una sola expresión. La programación funcional fomenta el uso de funciones puras, mientras que los lenguajes procedimentales hacen una distinción entre funciones que devuelven un valor y procedimientos que no lo hacen.

Finalmente, si bien esto puede parecer contradictorio al principio, Python tiene una declaración que no evalúa nada ni causa ningún efecto secundario. ¿Puedes adivinar qué es y cuándo querrías usarlo? Para darle una pequeña pista, en informática se le conoce comúnmente como no operación, que es la abreviatura de sin operación.

¿Adivina qué? ¡Es la declaración pass de Python! A menudo lo utiliza como marcador de posición en lugares donde se requiere sintácticamente una declaración, pero no desea realizar ninguna acción. Puede utilizar estos marcadores de posición en definiciones de funciones vacías o bucles durante las etapas iniciales de desarrollo. Tenga en cuenta que aunque los Ellipsis de Python (...) pueden tener un propósito similar, tiene un valor, lo que convierte a los Ellipsis en una expresión.

Resumen de expresiones vs declaraciones

Para conducir el punto a casa, eche un vistazo rápido al siguiente diagrama. Le ayudará a comprender mejor los diferentes tipos de expresiones y declaraciones en Python:

En resumen, una declaración puede ser cualquier instrucción de Python, ya sea una sola línea o un bloque de código. Los cuatro cuadrantes en el diagrama anterior representan declaraciones. Sin embargo, este término a menudo se usa para referirse a declaraciones puras que solo causan efectos secundarios sin tener un valor.

Por el contrario, una expresión pura es un tipo especial de declaración que solo se evalúa con algún valor sin causar efectos secundarios. Además, puede encontrar expresiones con efectos secundarios , que son declaraciones con un valor. Estas son expresiones que producen un valor al tiempo que causan efectos secundarios. Finalmente, un no-op es una declaración que tampoco, no produce un valor ni causa ningún efecto secundario.

A continuación, aprenderá a reconocerlos en la naturaleza.

¿Cómo verificar si una instrucción de Python es una declaración de expresión vs?

En este punto, ya sabe que cada instrucción de Python es técnicamente siempre una declaración. Por lo tanto, una pregunta más específica que quizás desee hacerse es si está tratando con una expresión o una declaración pura . Responderá a esta pregunta doble: manualmente y luego mediante programación utilizando Python.

Comprobando manualmente en Python REPL

Para determinar rápidamente la respuesta a la pregunta planteada en esta sección, puede aprovechar el poder de Python REPL, que evalúa expresiones a medida que las escribe. Si una instrucción resulta ser una expresión, verá inmediatamente la representación de cadena predeterminada de su valor en la salida:

>>> 42 + 8
50

Esta es una expresión aritmética, que se evalúa como 50. Como no ha interceptado su valor, por ejemplo, asignando la expresión a una variable o pasándola a una función como argumento, Python muestra el resultado calculado por usted. Si la misma línea de código hubiera estado presente en un script de Python, el intérprete habría ignorado el valor evaluado, que se habría perdido.

En contraste, ejecutar una declaración pura en el replica no muestra nada en la salida:

>>> import math
>>>

Esta es una declaración import , que no tiene un valor correspondiente. En cambio, carga el paquete Python especificado en su espacio de nombres actual como efecto secundario.

Sin embargo, debe tener cuidado con este enfoque. A veces, la representación de cadena predeterminada de un valor calculado puede ser engañosa. Considere este ejemplo:

>>> fruit = {"name": "apple", "color": "red"}
>>> fruit.get("taste")
>>> fruit.get("color")
'red'

Usted define un diccionario de Python que representa una fruta. La primera vez que llamas a .get(), no hay ningún resultado visible. Pero luego, llamas a .get() nuevamente con una clave diferente y devuelve el valor asociado con esa clave, que es 'red'.

En el primer caso, .get() devuelve Ninguno para indicar que falta un par clave-valor ya que el diccionario no contiene la clave "sabor". Sin embargo, cuando utiliza una clave que existe, como "color", el método devuelve el valor correspondiente.

El Python Reply nunca muestra None a menos que se imprima explícitamente, lo que a veces puede provocar confusión si no tiene conocimiento de este comportamiento. En caso de duda, siempre puede llamar a print() en el resultado para revelar su verdadero valor:

>>> fruit.get("taste")
>>> print(fruit.get("taste"))
None

Si bien esto funciona, hay una forma más confiable de verificar si una instrucción es una expresión en Python.

Puede asignar una instrucción a una variable para verificar si es un valor r. De lo contrario, si es una declaración pura, no podrá usarla como valor para su variable y obtendrá este error:

>>> x = import math
  File "<python-input-0>", line 1
    x = import math
        ^^^^^^
SyntaxError: invalid syntax

Python genera un SyntaxError para indicarle que no puede asignar una declaración import a una variable porque esa declaración no tiene un valor tangible.

Un caso algo especial es la asignación de cadena, que inicialmente puede parecer que está tratando de asignar y=42 a la variable x para verificar si es una expresión:

>>> x = y = 42

Sin embargo, esto hace que varias variables (x y y en este caso) se refieran al mismo valor, 42. Es una notación abreviada para realizar dos asignaciones separadas, x=42 y y=42.

Otra forma de saber si un trozo de código de pitón es una expresión o una declaración pura implica envolverlo en parentales . Los paréntesis generalmente le ayudan a agrupar los términos para cambiar el orden predeterminado de operaciones determinado por la precedencia del operador. Pero solo puedes envolver expresiones, no declaraciones:

>>> (2 + 2)
4

>>> (x = 42)
  File "<python-input-7>", line 1
    (x = 42)
     ^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

La línea 1 contiene una expresión, que se evalúa como cuatro, mientras que la línea 4 genera un error de sintaxis porque Python intenta sin éxito evaluar una declaración de asignación. El mensaje de error mostrado le solicita que cambie el operador de asignación (=) a igualdad de valor (==) o al operador morsa (:=).

Hasta ahora, has estado comparando expresiones y declaraciones manualmente en Python Repl. En la siguiente sección, aprenderá cómo hacer esto programáticamente, lo que puede ayudar si es una tarea repetitiva que desea automatizar.

Construyendo un detector de expresión de Python Vs Declar

Para determinar si una instrucción es una expresión o una declaración programada, puede llamar a las funciones integradas eval() y exec() incorporadas. La primera función le permite evaluar las expresiones dinámicamente, y la segunda puede ejecutar código arbitrario desde una cadena:

>>> eval("2 + 2")
4
>>> exec("x = 42")
>>> print(x)
42

La cadena "2 + 2" contiene una expresión de Python que eval() evalúa el número entero 4 . Puede asignar el valor devuelto por eval() a una variable si lo desea. Por otro lado, exec() no devuelve nada, pero causa efectos secundarios. Observe cómo puede acceder a la variable definida en la cadena pasada a exec() Después de llamar a esta función dentro del mismo alcance.

Ambas funciones informan un error de sintaxis cuando la cadena de entrada no es una expresión o declaración válida:

>>> eval("2 +")
Traceback (most recent call last):
  ...
SyntaxError: invalid syntax

>>> exec("x = 2 +")
Traceback (most recent call last):
  ...
SyntaxError: invalid syntax

Además, eval() genera un error de sintaxis cuando la cadena proporcionada contiene una declaración pura sin un valor. Al mismo tiempo, la misma declaración funciona bien con exec():

>>> eval("x = 2 + 2")
Traceback (most recent call last):
  ...
SyntaxError: invalid syntax

>>> exec("x = 2 + 2")

Puede aprovechar esta discrepancia para diferenciar entre expresiones y declaraciones puras. Pero recuerde llamar a eval() antes de exec() para evitar falsos positivos. Dado que todas las expresiones son declaraciones, exec() casi siempre tendrá éxito ya sea que reciba una expresión o una declaración como argumento:

>>> eval("print('Hello, World!')")
Hello, World!

>>> exec("print('Hello, World!')")
Hello, World!

Ambas funciones te dan un resultado idéntico: ¡Hola mundo!. Si llamó a exec() y se detuvo allí, podría concluir erróneamente que el código es una declaración, aunque podría ser una expresión válida. Mientras tanto, todas las llamadas a funciones en Python son técnicamente expresiones y deben clasificarse como tales. Así es como puedes abordar esto.

Para evitar la duplicación de código, puede combinar eval() y exec() en una sola función, que delega en ast.parse(). con el modo apropiado:

>>> import ast

>>> def valid(code, mode):
...     try:
...         ast.parse(code, mode=mode)
...         return True
...     except SyntaxError:
...         return False
...

>>> valid("x = 2 + 2", mode="eval")
False

>>> valid("x = 2 + 2", mode="exec")
True

Esta función acepta un fragmento de code de Python y el parámetro mode, que puede tomar uno de dos valores: "eval" o "exec ". La función devuelve False cuando la expresión o declaración subyacente es sintácticamente incorrecta. De lo contrario, devuelve True cuando el código se ejecuta correctamente en el modo especificado.

Para colmo, puede escribir otra función de ayuda que proporcione una representación textual para el fragmento de código dado:

>>> def describe(code):
...     if valid(code, mode="eval"):
...         return "expression"
...     elif valid(code, mode="exec"):
...         return "statement"
...     else:
...         return "invalid"
...

>>> describe("x = 2 + 2")
'statement'

>>> describe("2 + 2")
'expression'

>>> describe("2 +")
'invalid'

Primero verifica si el código dado es una expresión. Cuando es así, devuelve la cadena "Expresión" . Si no, entonces puede verificar si el código es una declaración. Si califica como una declaración, devuelve la cadena "Declaración" . Finalmente, si no se cumple ninguna condición, concluye que el código es "inválido" .

Este enfoque puede resultar útil cuando necesite realizar dicha prueba mediante programación por cualquier motivo. Tal vez esté creando su propio REPL de Python utilizando la coincidencia de patrones estructurales en Python y necesite decidir cuándo mostrar una expresión evaluada.

A estas alturas, debería comprender de forma más intuitiva la diferencia entre expresiones y declaraciones en Python. También deberías poder decir cuál es cuál. La siguiente pregunta importante es si se trata simplemente de una distinción semántica para los puristas o si tiene algún significado en la práctica diaria de codificación. ¡Lo descubrirás ahora!

¿Importa esta diferencia en su programación diaria?

La mayoría de las veces, no necesitas pensar demasiado si estás trabajando con una expresión o una declaración en Python. Dicho esto, hay dos excepciones notables que vale la pena mencionar:

  1. expresiones lambda
  2. afirmar declaraciones

Comenzará con el primero, que es la expresión lambda .

Expresión lambda

La palabra clave lambda de Python le permite definir una función anónima, que puede ser útil para operaciones de una sola vez, como especificar la clave de clasificación o una condición para filtrar:

>>> fruits = [("apple", 2), ("banana", 0), ("orange", 3)]

>>> sorted(fruits, key=lambda item: item[1])
[('banana', 0), ('apple', 2), ('orange', 3)]

>>> list(filter(lambda item: item[1] > 0, fruits))
[('apple', 2), ('orange', 3)]

Aquí se define una lista de tuplas de dos elementos que contienen el nombre de una fruta y su cantidad respectiva. A continuación, ordena esta lista en orden ascendente según la cantidad. Por último, filtras la lista dejando solo aquellas frutas que tengan al menos una unidad.

Este es un enfoque conveniente siempre y cuando no necesite hacer referencia a dichas funciones en línea más allá de su uso inmediato. Si bien siempre puedes asignar una expresión lambda a una variable para su uso posterior, no es sintácticamente equivalente a una función normal definida con def.

Una definición de función inicia un nuevo bloque de código que representa el cuerpo de la función. En Python, prácticamente cada bloque de código pertenece a una declaración compuesta, por lo que no se puede evaluar.

Debido a que las declaraciones no tienen valores, no puede asignar una definición de función a una variable como puede con una expresión de Lambda:

>>> inline = lambda: 42

>>> regular = (
...     def function():
...         return 42
... )
...
  File "<python-input-1>", line 2
    def function():
    ^^^
SyntaxError: invalid syntax

La variable inline contiene una referencia a una función anónima definida como lambda, mientras que regular es un intento fallido de asignar una definición de función con nombre a un variable.

Sin embargo, a usted puede se le permite asignar una referencia a una función que ya se ha definido en otra parte:

>>> def function():
...     return 42
...
>>> alias = function

Tenga en cuenta el significado diferente de la instrucción def function(): y la referencia function que aparece debajo. El primero es el plan de lo que hace la función, y la segunda es la dirección de la función, que puede pasar sin llamar a la función.

Una función definida con la palabra clave lambda siempre debe contener exactamente una expresión dentro de su cuerpo. Se evaluará y devolverá implícitamente sin que tenga que incluir la declaración return . De hecho, el uso de declaraciones en las funciones lambda está absolutamente prohibido. Si intenta usarlos, causará un error de sintaxis:

>>> lambda: pass
  File "<python-input-0>", line 1
    lambda: pass
            ^^^^
SyntaxError: invalid syntax

Ya has aprendido que pass es una declaración, por lo que no tiene lugar en una expresión lambda. Sin embargo, existe una solución para esto. Siempre puedes incluir una o más declaraciones en una función y llamar a esa función en tu expresión lambda, así:

>>> import tkinter as tk

>>> def on_click(age):
...     if age > 18:
...         print("You're an adult.")
...     else:
...         print("You're a minor.")
...

>>> window = tk.Tk()
>>> button = tk.Button(window, text="Click", command=lambda: on_click(42))
>>> button.pack(padx=10, pady=10)
>>> window.mainloop()

Esta es una aplicación Python mínima que presenta una interfaz gráfica de usuario (GUI) creada con Tkinter. La línea resaltada registra un oyente para el evento de clic del botón. El controlador de eventos se define como una expresión lambda en línea, que llama a una función contenedora que encapsula una declaración condicional. No se puede expresar una lógica tan compleja únicamente con una expresión lambda.

Afirmación de la declaración

En cuanto a la declaración afirmar , hay un punto de confusión común que lo rodea, que proviene de su sintaxis algo engañosa. Es demasiado fácil olvidar que afirmar es una declaración y no se comporta como una llamada de función ordinaria. Esto a veces puede causar un comportamiento involuntario cuando no tienes cuidado.

En su forma más básica, la palabra clave afirmar debe ser seguida por un predicado lógico o una expresión que evalúa un valor booleano:

>>> assert 18 < int(input("What's your age? "))
What's your age? 42

>>> assert 18 < int(input("What's your age? "))
What's your age? 15
Traceback (most recent call last):
  ...
AssertionError

Si la expresión se vuelve verdadera, entonces no pasa nada. Si la expresión es falsa, entonces Python genera un AssertionError, siempre que no haya deshabilitado las aserciones por completo con una opción de línea de comandos o una variable de entorno.

Opcionalmente, puede agregar un mensaje de error personalizado que se mostrará junto con el error de afirmación elevado. Para hacer eso, coloque una coma después del predicado e incluya una cadena literal, o cualquier expresión que se evalúe a una:

>>> assert 18 < int(input("What's your age? ")), "You're a minor"
What's your age? 15
Traceback (most recent call last):
  ...
AssertionError: You're a minor

Hasta ahora, todo bien. Sin embargo, incorporar un mensaje de error más largo puede generar un código menos legible, lo que lo tentará a romper esa línea de alguna manera.

Python ofrece una característica ordenada llamada Continuación de línea implícita, que le permite dividir una instrucción larga en múltiples líneas sin usar una barra invertida explícita (\). Puede lograr esto encerrando sus expresiones en parentheses , entre paréntesis o aparatos ortopédicos rizados , lo que hace que el código sea más organizado y más fácil de leer:

>>> assert (
...     18 < int(input("What's your age? ")),
...     "You're a minor"
... )
<python-input-0>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (
What's your age? 15
>>>

Esto generalmente funciona, pero no en este caso. Como puede ver, las versiones modernas de Python incluso emitirán una advertencia para hacerle saber que algo probablemente esté mal.

Recuerde que una declaración afirmar espera una expresión justo después de la palabra clave. Cuando rodea su mensaje predicado y personalizado con paréntesis, esencialmente define una tupla, que se trata como una expresión única. Python evalúa tales secuencias no vacías a true . Por lo tanto, su instrucción afirmar siempre pasará, independientemente de la condición real que esté tratando de verificar.

Para evitar este problema, es mejor que use una continuación de línea explícita cuando desea romper una línea larga:

>>> assert 18 < int(input("What's your age? ")), \
...     "You're a minor"
What's your age? 15
Traceback (most recent call last):
  ...
AssertionError: You're a minor

Ahora, observa nuevamente el comportamiento esperado porque el predicado se evalúa correctamente.

Anteriormente, aprendiste que los bloques de código en Python son parte de declaraciones compuestas. A continuación, explorará las diferencias entre declaraciones simples y compuestas en Python.

¿Qué son las declaraciones simples y compuestas en Python?

Las declaraciones son los componentes básicos de sus programas de Python. Le permiten controlar el flujo de ejecución y realizar acciones, como asignar valores a variables. Puede clasificar las declaraciones en dos tipos principales:

  1. Declaraciones simples
  2. Declaraciones compuestas

Las declaraciones simples pueden caber en una sola línea, mientras que las declaraciones compuestas comprenden otras declaraciones que típicamente abarcan múltiples líneas de código sangrado seguido de un carácter nuevo.

La siguiente tabla muestra algunos ejemplos comunes de ambos tipos de declaraciones:

assert age > 18

si edad > 18: ...

import math

mientras es verdadero: ...

return 42

para _ en el rango (3): ...

pass

intente: ...

x = 42 + 8

Def add (x, y): ...

Como puede ver, las declaraciones simples causan un efecto secundario particular: excepto la declaración pass , que no. Las declaraciones compuestas, por otro lado, incluyen construcciones de flujo de control tradicionales como bucles, condicionales y definiciones de funciones.

Cuando observa más de cerca las declaraciones compuestas, encontrará que constan de una o más cláusulas, cada una de las cuales contiene un encabezado y un conjunto. Considere la siguiente declaración condicional como ejemplo:

if age > 18:
    print("Welcome!")
elif age < 18:
    raise ValueError("You're too young to be here.")
else:
    import sys
    sys.exit(1)

Las líneas resaltadas representan los encabezados de la cláusula de la declaración compuesta, mientras que las líneas restantes representan sus suites correspondientes. Por ejemplo, la cláusula if verifica si la edad es mayor que dieciocho años, y llama condicionalmente la función print() dentro de su conjunto.

Todos los encabezados de cláusulas dentro de una declaración compuesta están alineados en el mismo nivel de sangría. Comienzan con una palabra clave de Python, como if, elif o else, y concluyen con dos puntos (:) que marca el inicio de un bloque de código de la suite. Una suite es una colección de declaraciones regidas por su respectiva cláusula. En este caso, las tres cláusulas determinan cuál de las suites ejecutar en función de la condición de edad.

Existe un tipo especial de declaración compuesta conocida como lista de declaraciones, que consta de una secuencia de declaraciones simples. Aunque cada una de ellas debe colocarse en una sola línea, puedes comprimir varias declaraciones simples en una sola línea. ¿Sabes cómo?

¿Cómo poner varias declaraciones en una sola línea?

En la mayoría de los casos, es preferible colocar solo una declaración o expresión por línea en aras de la legibilidad. Sin embargo, si insiste en ajustar más de uno en la misma línea, puede usar el semicolon (; ) como un separador de instrucción.

Un caso de uso popular en el que esto podría ser útil implica ejecutar un programa de una línea en la línea de comandos utilizando la opción python -c :

$ python -c 'import sys; print(sys.version)'
3.13.0 (main, Oct 19 2024, 15:05:58) [GCC 13.2.0]

Esto le permite probar ideas rápidamente en forma de fragmentos de código cortos. Pero hoy en día, la mayoría de las terminales te permiten distribuir el código en varias líneas sin ningún problema:

$ python -c '
> import sys
> print(sys.version)
> '
3.13.0 (main, Oct 19 2024, 15:05:58) [GCC 13.2.0]

El comando que el intérprete de Python espera puede consistir en múltiples líneas.

Otro caso de uso común del punto y coma es insertar un punto de interrupción en su código. Antes de Python 3.7, que introducía la función breakpoint() incorporada, saltabas al depurador con la siguiente línea de código idiomática:

import pdb; pdb.set_trace()

Cuando el intérprete presiona pdb.set_trace(), pausará la ejecución y usted ingresará a la sesión de depuración interactiva utilizando el depurador de Python (pdb).

Además, puedes intentar usar este truco para ofuscar o minimizar intencionalmente tu código Python hasta cierto punto. Sin embargo, no podrás evadir algunas limitaciones sintácticas, por lo que obtendrás mejores resultados con herramientas externas como Pyarmor.

Antes de cerrar este tutorial, debe responder una última pregunta sobre expresiones y declaraciones en Python. Uno que le dará la base para abordar los desafíos de programación más avanzados.

¿Pueden las declaraciones tener una doble naturaleza en Python?

Dado que todas las instrucciones en Python son declaraciones, puede ejecutar una expresión que tenga efectos secundarios sin considerar el valor calculado. Este es a menudo el caso cuando llama a una función o método, pero ignora su valor de retorno:

>>> with open("file.txt", mode="w", encoding="utf-8") as file:
...     file.write("Hello, World!")
...
13

En el ejemplo anterior, llame al método .WRITE() con el método String "¡Hola, mundo!" como argumento. Aunque este método devuelve el número de caracteres escritos, que es trece, los ignora al no interceptar el valor devuelto en una variable o procesarlo más de ninguna manera. Observe, sin embargo, que el Python REPL muestra automáticamente el resultado de la última expresión evaluada en esta situación.

Bueno. Por lo tanto, puede aprovechar las expresiones únicamente por sus efectos secundarios, si los tienen. Pero ¿qué pasa al revés? ¿Puedes evaluar declaraciones? ¡Estás a punto de descubrirlo!

Asignaciones

Algunos lenguajes de programación difuminan las líneas entre declaraciones y expresiones. Por ejemplo, puede evaluar una declaración de asignación en C o C++:

#include <stdio.h>

int main() {
    int x;
    while (x = fgetc(stdin)) {
        if (x == EOF)
            break;
        putchar(x);
    }
    return 0;
}

Este breve programa repite todo lo que el usuario escribe en el teclado. Observe la línea resaltada, que obtiene el siguiente carácter de la entrada estándar y lo asigna a una variable local, x. Al mismo tiempo, esta asignación se evalúa como una expresión booleana y se utiliza como condición de continuación para el bucle while. En otras palabras, mientras el número ordinal del carácter ingresado sea distinto de cero, el ciclo continúa.

Esta ha sido una fuente notoria de errores que ha afectado a los programas C y C++ durante años. Los programadores a menudo usarían por error el operador de asignación (=) en tales condiciones en lugar del operador de prueba de igualdad previsto (==) debido a su parecido visual. Aunque el ejemplo anterior utiliza esta característica intencionalmente, normalmente fue el resultado de un error humano que podría provocar un comportamiento inesperado.

Durante mucho tiempo, los desarrolladores principales evitaron implementar una característica similar en Python debido a preocupaciones sobre esta posible confusión. Ese fue el caso hasta Python 3.8, que introdujo el operador morsa (:=) para permitir expresiones de asignación:

MAX_BYTES = 1024

buffer = bytearray()
with open("audio.wav", mode="rb") as file:
    while chunk := file.read(MAX_BYTES):
        buffer.extend(chunk)

En este fragmento de código, lee incrementalmente un archivo WAV en fragmentos binarios hasta encontrar el byte de control de fin de archivo, que se indica mediante un fragmento vacío. Sin embargo, en lugar de comprobar explícitamente si el fragmento devuelto por .read() no está vacío (o si se evalúa como True), se aprovecha la expresión de asignación para ejecutar y evaluar. la tarea en un solo paso.

Esto le ahorra algunas líneas de código, que de otro modo se habría visto así:

MAX_BYTES = 1024

buffer = bytearray()
with open("audio.wav", mode="rb") as file:
    while True:
        chunk = file.read(MAX_BYTES)
        if not chunk:
            break
        buffer.extend(chunk)

Convirtió un bucle determinista en uno infinito y agregó tres declaraciones más: la declaración de asignación, la declaración condicional y la declaración break .

A diferencia del ejemplo de C, no hay forma de confundir la expresión de asignación con su contraparte de declaración. Python continúa rechazando declaraciones de asignación en un contexto booleano. En tales ocasiones, debes usar explícitamente el operador morsa, que se alinea muy bien con uno de los aforismos del Zen de Python:

Lo explícito es mejor que lo implícito. (Fuente)

Hay otros ejemplos de declaraciones que tienen sus análogos de expresión en Python. A continuación, buscará declaraciones y expresiones condicionales.

Condicionales

Muchos lenguajes de programación proporcionan un operador condicional ternario , que combina tres elementos: una condición lógica, un valor si la condición se evalúa a true , y un valor alternativo en caso de que la condición evalúe la evaluación falso .

En los idiomas de la familia C, el operador ternario (?: ) se asemeja al emoticón de Elvis Presley con su peinado distintivo. Mientras que esos idiomas lo llaman operador de Elvis, Python se adhiere a la expresión condicional del término más conservador. Esto es lo que parece:

>>> def describe(users):
...     if not users:
...         print("No people")
...     else:
...         print(len(users), "people" if len(users) > 1 else "person")
...

>>> describe([])
No people

>>> describe(["Alice"])
1 person

>>> describe(["Alice", "Bob"])
2 people

A primera vista, la expresión condicional de Python se parece a la declaración condicional estándar condensada en una sola línea. Comienza con una expresión asociada con un valor verdadero, seguida de la condición a verificar y luego la expresión correspondiente a un valor falso. Le permite evaluar la lógica condicional, que normalmente requeriría el uso de una declaración condicional.

Comprensiones

Otro ejemplo de declaraciones que ocupan el área gris son todo tipo de expresiones de comprensión , como la comprensión de la lista o la expresión del generador, que puede aprovechar para evitar bucles explícitos:

>>> fruits = [("apple", 2), ("banana", 0), ("orange", 3)]
>>> sorted(name.title() for name, quantity in fruits if quantity > 0)
['Apple', 'Orange']

Nuevamente, la sintaxis es similar al bucle for y a la instrucción if, pero los colapsas en una línea y reorganizas sus partes individuales. Esto sólo funciona bien cuando la condición y la expresión a evaluar son razonablemente pequeñas para que puedan caber en una o dos líneas. De lo contrario, terminará con un código desordenado que será difícil de leer.

Por último, los generadores de Python merecen mención aquí porque usan sintaxis que puede ser tanto una expresión como una declaración al mismo tiempo.

Generadores

El rendimiento y rendimiento de las palabras clave aparecen dentro de las funciones del generador, que le permiten manejar grandes flujos de datos de manera eficiente o definir las corutinas.

Dejas que Python ejecute yield como una declaración cuando quieres producir valores a partir de una función generadora:

>>> import random

>>> def generate_noise(size):
...     for _ in range(size):
...         yield 2 * random.random() - 1
...

>>> list(generate_noise(3))
[0.7580438973021972, 0.5273057193944659, -0.3041263216813208]

Este generador es un productor de valores aleatorios entre -1 y 1. Usando rendimiento Aquí es algo similar a usar la declaración return . Reemplace una serie de valores a la persona que llama como efecto secundario, pero en lugar de terminar la función por completo, la declaración rendimiento suspende la ejecución de la función, lo que le permite reanudar y continuar más tarde.

Ahora, opcionalmente, puedes evaluar el rendimiento como una expresión para convertir tu generador en un prosumidor (productor y consumidor) con potencialmente muchos puntos de entrada y salida. Cada expresión yield puede proporcionar y recibir valores en ambos sentidos. Los valores generados representan la salida del generador, mientras que los valores enviados de regreso al generador son la entrada:

>>> def lowpass_filter():
...     a = yield
...     b = yield a
...     while True:
...         a, b = b, (yield (a + b) / 2)
...

La primera expresión rendimiento no produce implícitamente nada ( none ) pero recibe un valor del mundo exterior cuando se evalúa como una expresión. Este valor luego se asigna a una variable local llamada a .

El segundo rendimiento expresión produce a , mientras almacena simultáneamente otro valor en b . En este punto, el filtro se sature y se está listo para procesar los datos entrantes. La capacidad de consumir y producir valores en cada punto de suspensión ( rendimiento ) permite empujar una señal a través de su filtro como una corriente eléctrica fluye a través de un circuito.

La última expresión rendimiento calcula y produce el promedio de ambos números, sobrescribe a con b , y luego asigna un nuevo valor a b .

Puedes conectar ambos generadores entre sí llamando a .send() en tu filtro de paso bajo, que calcula un promedio móvil de dos puntos que suaviza la señal:

>>> lf = lowpass_filter()
>>> lf.send(None)
>>> for value in generate_noise(10):
...     print(f"{value:>5.2f}: {lf.send(value):>5.2f}")
...
 0.66:  0.66
-0.01:  0.32
 0.30:  0.15
-0.10:  0.10
-0.98: -0.54
 0.79: -0.10
 0.31:  0.55
-0.10:  0.11
-0.25: -0.18
 0.57:  0.16

Los números en la columna de la izquierda provienen directamente del generador de ruido y se envían directamente al filtro de paso bajo. La columna de la derecha contiene la salida de ese filtro. Tenga en cuenta que primero debe preparar a su prosumidor enviando None o llamando a next() para que avance a la primera expresión yield.

Conclusión

En este tutorial, ha explorado las diferencias fundamentales entre expresiones y declaraciones en Python. Ha aprendido que las expresiones son piezas de sintaxis que se evalúan como un valor, mientras que las declaraciones son instrucciones que pueden causar efectos secundarios. Al mismo tiempo, hay una zona gris entre ellos.

Comprender esta distinción es crucial para los desarrolladores de Python, ya que afecta la forma en que escribe y estructura su código. Ahora que tiene estas habilidades, puede escribir código Python más preciso y expresivo, aprovechando al máximo las expresiones y declaraciones para crear programas sólidos y eficientes.