diff --git a/modules/10-basics/10-hello-world/es/README.md b/modules/10-basics/10-hello-world/es/README.md index f9db33c5..bc62677d 100644 --- a/modules/10-basics/10-hello-world/es/README.md +++ b/modules/10-basics/10-hello-world/es/README.md @@ -1,7 +1,58 @@ -Por lo general, el aprendizaje de un nuevo lenguaje de programación comienza con 'Hello, World!'. Este es un programa simple que muestra un saludo en la pantalla, y al mismo tiempo, familiariza al usuario con el nuevo lenguaje, así como con su sintaxis y la estructura del programa. +El aprendizaje de un nuevo lenguaje de programación empieza tradicionalmente con el programa 'Hello, World!'. Es un programa simple que muestra un saludo en la pantalla y presenta la sintaxis y la estructura del nuevo lenguaje. ```text Hello, World! ``` -Esta tradición tiene más de cuarenta años, por lo cual no la romperemos. En la primera lección escribiremos el programa `Hello, World!`. Para hacerlo, debemos darle al ordenador un comando especial. En Python, este comando se llama `print()`. +![Hello World](./assets/hello-world.png) + +Esta tradición tiene ya más de cuarenta años, y nosotros también empezaremos por ella. En la primera lección escribiremos el programa `Hello, World!`. En Python ese programa se ve así: + +```python +print('Hello, World!') +``` + +El comando `print()` muestra en la pantalla el texto indicado entre paréntesis. En lugar del ejemplo se puede escribir cualquier otro texto. + +```python +print('Hexlet - escuela de programación') +``` + +El comando sigue siendo el mismo, solo cambia el contenido de los paréntesis. Para que el programa entienda que se trata precisamente de texto, este se encierra entre comillas. Se pueden usar comillas simples `'...'` o dobles `"..."`, pero la comilla de apertura y la de cierre deben coincidir. + +```python +print("Hexlet - escuela de programación") +``` + +Según el estándar de estilo de código aceptado en Python (PEP 8), se recomienda usar comillas simples para las cadenas. Si dentro de la cadena hay un apóstrofo, las comillas simples romperán la sintaxis, por lo que en esos casos se usan las dobles. + +```python +print("it's a Python") # apóstrofo dentro, por eso comillas dobles +``` + +## El significado de los símbolos + +El código está formado por comandos, y cada uno de ellos debe escribirse de una forma determinada. Además de las letras, en el código son importantes las comillas `'` y `"`, los paréntesis `()` y los signos de puntuación. Un signo omitido o confundido hará que el programa no se ejecute. ¿Puedes intentar determinar qué error se cometió en cada una de las líneas? + +```python +print("it's a Python" +print(it's a Python") +prin("it's a Python") +print('it's a Python") +prInt("it's a Python") +``` + +Incluso una pequeña diferencia, por ejemplo una letra de más o un signo distinto, puede hacer que el programa no funcione. Esto también se aplica a las mayúsculas y minúsculas, es decir, a la diferencia entre letras grandes y pequeñas. Mientras que en el texto ordinario `Hola` y `hola` se ven iguales, para Python son palabras diferentes. Python considera `print`, `Print` y `PRINT` como comandos diferentes, y solo funcionará la primera variante. + +## Dónde practicar + +La teoría se asimila mejor cuando en paralelo ejecutas código y ves el resultado. Para eso sirve la consola interactiva de Python (REPL), donde los comandos se ejecutan línea por línea. Todo lo que aparece en la lección conviene probarlo [en la consola interactiva de Python](https://pyodide.org/en/stable/console.html). + +¿Cómo funciona esto técnicamente? Cualquier código escrito se pasa al intérprete de Python, que lo ejecuta y muestra en la pantalla el resultado de su trabajo. + +```text +Código Intérprete Pantalla +┌──────────┐ ┌─────────────┐ ┌──────────────┐ +│ print(…) │ ──→ │ Python │ ──→ │ Hello, World!│ +└──────────┘ └─────────────┘ └──────────────┘ +``` diff --git a/modules/10-basics/10-hello-world/es/assets/hello-world.png b/modules/10-basics/10-hello-world/es/assets/hello-world.png new file mode 100644 index 00000000..9bf2299f Binary files /dev/null and b/modules/10-basics/10-hello-world/es/assets/hello-world.png differ diff --git a/modules/10-basics/20-comments/es/README.md b/modules/10-basics/20-comments/es/README.md index bf33837f..44ec55a3 100644 --- a/modules/10-basics/20-comments/es/README.md +++ b/modules/10-basics/20-comments/es/README.md @@ -1,22 +1,57 @@ - -Casi todos los lenguajes de programación permiten dejar comentarios en el código. Estos comentarios no son utilizados por el sistema intérprete. Son útiles únicamente para las personas que usan estas herramientas; para que los programadores puedan dejar notas para ellos mismos y también para otros programadores. - -A través de estos comentarios, se agregan explicaciones sobre cómo funciona el código, qué errores corregir o qué agregar más adelante. +Además del propio código, en los archivos fuente se encuentran a menudo comentarios. Son líneas que el intérprete no procesa. Los programadores las escriben para explicar cómo funciona el código, señalar errores o recordar qué queda por terminar. ```python # Eliminar la línea de abajo después de implementar la tarea de registro print(10) ``` -Los comentarios en Python comienzan con el signo `#` y pueden aparecer en cualquier lugar del programa. Pueden ocupar toda una línea. Si una línea no es suficiente, se pueden crear varios comentarios: +En Python todos los comentarios son de una sola línea. Empiezan con el símbolo especial #, tras el cual puede ir cualquier texto. Todo lo escrito después de # lo ignora el intérprete. + +```text +# comentario ──→ [ omitido por el intérprete ] +print('hello') ──→ [ ejecutado → hello ] +# otro más ──→ [ omitido por el intérprete ] +``` + +Un comentario puede ocupar toda la línea: ```python -# ¡For Winterfell! -# ¡For Lannisters! +# For Winterfell! +# For Lanisters! ``` -Un comentario puede estar en la línea después del código: +O estar al final de una línea con código: ```python -print('I am the King') # ¡For Lannisters! +print('I am the King') # For Lannisters! ``` + +Si hace falta dejar una explicación larga, se usan varias líneas con #: + +```python +# The night is dark and +# full of terrors. +print('I am the King') +``` + +El intérprete ignora los comentarios. Los desarrolladores, gracias a ellos, entienden más rápido el código ajeno y no olvidan detalles importantes en el propio. + +## Comentarios de servicio + +Durante el trabajo te encontrarás con este código en nuestro editor: + +```python +# BEGIN + +# END +``` + +*BEGIN* y *END* aquí son comentarios normales que no influyen de ninguna manera en el funcionamiento del programa. Muestran dónde escribir el código de la tarea. + +```python +# BEGIN + +# END +``` + +Cuando veas *BEGIN* y *END*, escribe tu código entre ellos y deja el resto sin cambios. diff --git a/modules/10-basics/30-instructions/es/README.md b/modules/10-basics/30-instructions/es/README.md index c56efbb1..a406883f 100644 --- a/modules/10-basics/30-instructions/es/README.md +++ b/modules/10-basics/30-instructions/es/README.md @@ -1,26 +1,69 @@ -Cuando preparamos una comida, seguimos la receta al pie de la letra. De lo contrario, la comida no saldrá como esperábamos. Esta regla también se aplica a la programación. +Al comando `print('Hexlet')` podemos llamarlo instrucción: le dice al intérprete de Python qué hay que hacer. Esas instrucciones pueden ser tantas como se quiera. Cada una se ejecuta después de que ha terminado la anterior, y así, a partir de elementos simples, obtenemos un programa tan grande y complejo como queramos. -Para ver el resultado esperado en la pantalla, es necesario darle al ordenador instrucciones claras, paso a paso. Una instrucción es un comando para el ordenador; una unidad de ejecución. En este caso, el código en Python es un conjunto de instrucciones. Se puede representar como una receta de cocina paso a paso. +```text +Instrucción 1: print('Hello') → ejecutada + ↓ +Instrucción 2: print('World') → ejecutada + ↓ +Instrucción 3: print('!') → ejecutada +``` + +Aquí tienes un ejemplo de código con dos instrucciones. Estas líneas le dicen al ordenador que muestre las frases en la pantalla. + +```python +print('Mother of Dragons.') # Primera instrucción +print('Dracarys!') # Segunda instrucción +``` -El código en Python es ejecutado por un **intérprete** - es decir, un programa que ejecuta las instrucciones recibidas en orden. Al igual que los pasos en una receta, el conjunto de instrucciones para el intérprete se escriben en orden y se separan por saltos de línea. +El resultado de la ejecución: -Los desarrolladores deben entender el orden de las acciones en el código y ser capaces de dividir mentalmente el programa en partes independientes, fáciles de analizar. +```text +Mother of Dragons. +Dracarys! +``` -Veamos un ejemplo de código con dos instrucciones. Al ejecutarlo, se mostrarán en la pantalla dos frases en secuencia: +## El orden importa + +El intérprete de Python ejecuta el código estrictamente en el orden en que lo escribiste. Si se intercambian las líneas: ```python -print('Mother of Dragons.') print('Dracarys!') -# => Mother of Dragons. -# => Dracarys! +print('Mother of Dragons.') ``` -Anteriormente hemos señalado que las instrucciones se separan por saltos de línea. Pero también hay otra forma: se pueden separar por punto y coma (`;`): +en la pantalla también se intercambiarán: + +```text +Dracarys! +Mother of Dragons. +``` + +## Forma alternativa de escritura + +Normalmente las instrucciones se escriben en líneas separadas, pero Python permite unir varias instrucciones en una línea mediante `;`: ```python -print('Mother of Dragons.'); print('Drakarys!') +print('Mother of Dragons.'); print('Dracarys!'); ``` -No hay diferencia técnica alguna entre la primera y la segunda opción: el intérprete entenderá las instrucciones de la misma manera. La única diferencia es que la segunda opción puede ser incómoda de leer para los humanos. +Las dos versiones funcionan igual, pero la segunda variante es más difícil de leer. Por eso las instrucciones casi siempre se escriben una por línea. + +## Para qué hace falta esto + +Ahora escribimos programas muy simples, pero con el tiempo empezarán a complicarse, y una de las habilidades más importantes que ayudará a entenderlos es la capacidad de dividir (mentalmente) el programa en instrucciones independientes. Solo así se puede entender qué ocurre en el código. Abajo hay un ejemplo para llamar la atención; entenderlo por ahora no hace falta: + +```python +def is_prime(number: int) -> bool: + if number < 2: + return False + + divider = 2 + + while divider <= number / 2: + if number % divider == 0: + return False -Es mejor colocar las instrucciones una debajo de la otra. De esta manera, será más fácil para tus colegas leer tu código, mantenerlo y realizar cambios en él. + divider += 1 + + return True +``` diff --git a/modules/10-basics/40-testing/es/README.md b/modules/10-basics/40-testing/es/README.md index 96fd5094..2e7633ca 100644 --- a/modules/10-basics/40-testing/es/README.md +++ b/modules/10-basics/40-testing/es/README.md @@ -1,28 +1,36 @@ +Nuestro sitio comprueba automáticamente tus soluciones. ¿Cómo funciona? -Nuestro sitio web verifica automáticamente tus soluciones (uso específico del término en programación). ¿Cómo funciona? +En el caso más simple, la comprobación ejecuta tu código y compara la salida en pantalla con el resultado esperado. Por ejemplo, si la tarea dice: «Muestra el número 10 en la pantalla», tu código en Python puede verse así: -En los casos más simples, el sistema simplemente ejecuta tu código y verifica lo que se muestra en la pantalla. Luego lo compara con lo que "esperábamos" según la tarea. +```python +print(10) +``` + +La comprobación ejecutará ese código y verificará que en la pantalla apareció realmente `10`. Si la salida coincide con la esperada, la solución se acepta. Si no, verás un error: -En las lecciones más avanzadas, escribirás funciones, que son pequeños programas que toman información del mundo exterior y realizan operaciones con base en esta información. La verificación de tus soluciones en estos casos es un poco más complicada: el sistema ejecuta tu solución y le pasa cierta información. El sistema también sabe qué respuesta debería dar la función correcta con esos datos de entrada. +```bash +E AssertionError: assert '9' == '10' +E +E - 10 +E + 9 +``` -Por ejemplo, si tu tarea es escribir una función que sume dos números, el sistema de verificación le pasará diferentes combinaciones de números y comparará la respuesta de tu función con las sumas reales. Si las respuestas coinciden en todos los casos, se considera que la solución es correcta. +La línea con `+` muestra el resultado real que devolvió tu código, y la línea con `-` el valor esperado. -Este enfoque se llama pruebas y se utiliza en el desarrollo diario real. Por lo general, el programador primero escribe una prueba, que es un programa de verificación, y luego escribe el programa que quería escribir. Durante el proceso, ejecuta las pruebas constantemente y verifica si se acerca a la solución. +En las lecciones siguientes, más complejas, escribirás funciones. Reciben datos y devuelven un resultado. En esas tareas la comprobación funciona un poco de otra manera: llama a tu función con distintos argumentos y sabe de antemano qué respuesta debe salir en cada caso. -Es por eso que nuestro sitio web indica "Pruebas aprobadas" cuando resuelves correctamente un problema. +Por ejemplo, si hay que escribir una función que sume dos números, la comprobación le pasará distintos pares de números y comparará el resultado con la suma correcta. Si en todos los casos las respuestas coinciden, la solución se considera correcta. -Aquí tienes un ejemplo sencillo: en una de las futuras lecciones, tendrás que escribir una función que realice cálculos y devuelva una respuesta. Supongamos que cometiste un pequeño error y la función devolvió un número incorrecto. El sistema responderá algo así: +Ese enfoque se llama pruebas, y se usa en el desarrollo real. Las pruebas ayudan a comprobar si el programa funciona correctamente y a detectar rápido un error después de los cambios. -```text -AssertionError: '10' != '35' -``` +Precisamente por eso nuestro sitio dice «Pruebas aprobadas» cuando has resuelto bien la tarea. -Lo más importante sigue después de los dos puntos: "el valor '10' no es igual al valor esperado '35'". Es decir, la función correcta debería haber devuelto 35, pero la solución actual no funciona correctamente y devuelve 10. +## ¿Es mi error o no? -También vale la pena mencionar que si ves que en el editor ya hay algún código junto con los comentarios `BEGIN` y `END`, generalmente significa que debes escribir tu código entre estos comentarios. No debes modificar de antemano el código que se te proporciona, ya que esto puede afectar la verificación de la solución. En resumen, si ves líneas con los comentarios `BEGIN` y `END`, ¡escribe tu código en el espacio que se encuentra entre estos! +A veces, durante la resolución, parecerá que hiciste todo correctamente pero la comprobación no acepta la solución. Eso ocurre en muy raras ocasiones. Las pruebas se ejecutan automáticamente después de cada cambio, así que una comprobación rota normalmente no llega al sitio. ---- +En la inmensa mayoría de esos casos el error está en el código de la solución. Puede ser muy poco visible: en lugar de una letra inglesa se escribió por accidente una rusa, en lugar de mayúscula se usó minúscula, o se olvidó mostrar una coma. También hay situaciones más complicadas. Por ejemplo, la solución funciona para un conjunto de datos de entrada, pero no para otro. -A veces, durante el proceso de resolución, puede parecer que has hecho todo correctamente, pero el sistema se comporta de manera "caprichosa" y no acepta la solución. Este comportamiento es prácticamente imposible. Las pruebas no funcionales simplemente no pueden llegar al sitio web; se ejecutan automáticamente después de cada cambio. En la gran mayoría de estos casos (y todos nuestros proyectos en conjunto han realizado millones de verificaciones a lo largo de los años), el error se encuentra en el código de la solución. Puede ser muy sutil, como haber ingresado accidentalmente una letra rusa en lugar de una letra en inglés; usar minúsculas en lugar de mayúsculas; o olvidar poner una coma, por ejemplo. Otros casos son más complicados. Es posible que tu solución funcione para un conjunto de datos de entrada, pero no funcione para otro. Por lo tanto, siempre lee atentamente la descripción del problema y los resultados de las pruebas. Casi seguro que habrá alguna indicación sobre el error. +Por eso lee siempre con atención el enunciado de la tarea y la salida de las pruebas. Ahí casi con seguridad hay una indicación del error. -Sin embargo, si estás seguro de que hay un error o has encontrado alguna imprecisión, siempre puedes señalarlo. Al final de cada teoría hay un enlace al contenido de la lección en GitHub (¡este proyecto es completamente abierto!). Al ir allí, puedes escribir un issue, ver el contenido de las pruebas (donde se muestra cómo se llama tu código) e incluso enviar un pull request. Si esto aún te resulta confuso, únete a nuestra [comunidad en Telegram](https://t.me/hexletcommunity/3), allí en el canal *'Feedback'* siempre estaremos dispuestos a ayudar. +Y si estás seguro de que el problema está en la tarea o encontraste una imprecisión, escribe a nuestra [comunidad](https://t.me/hexletcommunity), al canal _'Feedback'_. diff --git a/modules/10-basics/50-syntax-errors/es/README.md b/modules/10-basics/50-syntax-errors/es/README.md index 92f9fc42..044460cb 100644 --- a/modules/10-basics/50-syntax-errors/es/README.md +++ b/modules/10-basics/50-syntax-errors/es/README.md @@ -1,15 +1,33 @@ +Si un programa en Python está escrito violando las reglas, el intérprete detendrá la ejecución y mostrará un mensaje de error. En ese mensaje se indica: -Si un programa escrito en Python tiene errores de sintaxis, el intérprete mostrará un mensaje correspondiente en la pantalla. También indicará el archivo y la línea donde se produjo el error. +- El tipo de error, +- la línea en la que ocurrió, +- y (a menudo) el punto donde el intérprete «se tropezó». -Un **error de sintaxis** ocurre cuando el código se ha escrito con violación de las reglas gramaticales. En los lenguajes naturales, la gramática es importante, pero generalmente se puede entender y leer el texto con errores. En la programación, todo es estricto. El más mínimo error y el programa ni siquiera se ejecutará. Un ejemplo puede ser un punto y coma olvidado, paréntesis mal colocados y otros detalles. +## ¿Qué es un error de sintaxis? -Aquí hay un ejemplo de código con un error de sintaxis: +Un error de sintaxis (SyntaxError) es una violación de las reglas de escritura del código (las reglas gramaticales) en un lenguaje de programación concreto. Esos errores aparecen si el código está escrito desviándose del formato esperado, por ejemplo si no se cerró una cadena, se omitió un paréntesis, se alteró el orden de los símbolos, etc. + +![Errores](./assets/errors-python.png) + +A diferencia de las lenguas naturales, donde un texto con errores se puede entender por el contexto, en programación incluso la mínima desviación deja el código inservible. + +```text +Código con error Intérprete Resultado +┌──────────────┐ ┌─────────────┐ ┌──────────────────┐ +│ print('Hi' │ ──> │ Python │ ──> │ SyntaxError: │ +└──────────────┘ └─────────────┘ │ unexpected EOF │ + └──────────────────┘ +``` + +Veamos un ejemplo simple con un error de sintaxis: ```python +# La variante correcta es esta: print('Hodor') print('Hodor) ``` -Si ejecutamos el código anterior, veremos el siguiente mensaje: +En este código no se cerró la comilla, lo que hace que el programa sea incorrecto desde el punto de vista de la sintaxis. Intentemos ejecutar el programa y el intérprete dará un error: ```bash python index.py @@ -19,4 +37,19 @@ File "index.py", line 1 SyntaxError: EOL while scanning string literal ``` -Por un lado, los errores de sintaxis son los más simples, porque están relacionados con las reglas gramaticales de escribir código, no con el significado del código. Son fáciles de corregir: solo necesitas encontrar la violación en la escritura. Por otro lado, el intérprete no siempre puede señalar claramente esta violación. Por lo tanto, a veces es necesario colocar el paréntesis olvidado no donde indica el mensaje de error. +El texto, por falta de costumbre, puede resultar incomprensible, pero eso es normal: cuanto más te encuentres con esos errores, más entenderás a primera vista qué ocurrió. + +## ¿Por qué esos errores se consideran simples? + +Los errores de sintaxis: + +- son fáciles de notar: el código a menudo se resalta en el editor; +- son fáciles de corregir: basta con devolver el símbolo omitido o arreglar la estructura. + +Pero hay un pero. El intérprete no siempre señala exactamente el lugar donde se cometió el error. A veces el problema está unas líneas más arriba. Por ejemplo, un paréntesis abierto pero no cerrado en una línea puede «romper» todo el código siguiente. + +## ¿Qué hacer ante un error de sintaxis? + +- Lee el mensaje de error. Casi siempre contiene información útil. +- Comprueba la línea indicada en el mensaje y la línea anterior: a veces el error está «escondido» un poco antes. +- Usa un [editor con resaltado de sintaxis](https://code.visualstudio.com/): te ayudará a notar los problemas de inmediato (por ejemplo, comillas o paréntesis sin cerrar). diff --git a/modules/10-basics/50-syntax-errors/es/assets/errors-python.png b/modules/10-basics/50-syntax-errors/es/assets/errors-python.png new file mode 100644 index 00000000..60b0d8c5 Binary files /dev/null and b/modules/10-basics/50-syntax-errors/es/assets/errors-python.png differ diff --git a/modules/20-arithmetics/20-basic/es/README.md b/modules/20-arithmetics/20-basic/es/README.md index 0e719df8..6d86f0c2 100644 --- a/modules/20-arithmetics/20-basic/es/README.md +++ b/modules/20-arithmetics/20-basic/es/README.md @@ -1,47 +1,138 @@ -A nivel básico, las computadoras operan solo con números. Incluso en aplicaciones de alto nivel, hay muchos números y operaciones dentro de ellas. Pero para empezar, es suficiente conocer la aritmética básica, y eso es lo que haremos. +En el nivel básico, los ordenadores solo trabajan con números. Incluso si escribes una aplicación compleja en un lenguaje de programación moderno, dentro de ella siempre ocurren numerosos cálculos: sumas, restas, divisiones, etc. -Por ejemplo, en matemáticas, escribimos la suma de dos números como: `3 + 4`. En programación, es lo mismo. Aquí hay un programa que suma dos números: +![Palillos de contar](./assets/arithmetics.png) + +Por suerte, para empezar a programar basta con conocer la aritmética escolar corriente. Por ella empezaremos. + +## La suma en Python + +En matemáticas, para sumar escribimos 3 + 4. En Python es exactamente igual: ```python 3 + 4 ``` -La aritmética en programación es prácticamente igual a la aritmética escolar. +Ese código se puede ejecutar realmente: el intérprete hará el cálculo. Pero… no hará nada con el resultado. Es decir, el 7 se obtendrá, pero tú no lo verás. -La línea de código `3 + 4` hace que el intérprete sume los números y obtenga el resultado. Este programa funcionará, pero no tiene sentido. Básicamente, no le estamos dando una instrucción al intérprete, simplemente le estamos diciendo: "mira, esta es la suma de tres y cuatro". En un trabajo real, no es suficiente informar al intérprete sobre una expresión matemática. +## Para ver el resultado hay que mostrarlo -Por ejemplo, si estás creando una tienda en línea, no es suficiente pedirle al intérprete que calcule el costo de los productos en el carrito. Necesitas pedirle que calcule el costo **Y** mostrarle el precio al comprador. +En un programa real no basta con calcular el valor. Hay que hacer algo con el resultado, por ejemplo mostrárselo al usuario. -Necesitamos pedirle al intérprete que sume `3 + 4` **Y** además darle una instrucción para hacer algo con el resultado. Por ejemplo, mostrarlo en la pantalla: +Para eso usamos el ya habitual comando `print()`, al que en adelante llamaremos función: ```python -# Primero se calcula la suma, -# luego, se pasa a la función de impresión print(3 + 4) ``` -Después de ejecutarlo, aparecerá el resultado en la pantalla: +Aquí primero se calcula la suma y después se pasa a la función de impresión. + +```text +print(3 + 4) + └─┬─┘ + 7 + +print(7) → 7 +``` + +El resultado de la ejecución: ```text 7 ``` -Además de la suma, están disponibles las siguientes operaciones: +Si se escribe esa misma expresión como cadena, obtendremos un resultado completamente distinto: se mostrará la cadena «tal cual»: + +```python +print('3 + 4') # muestra: 3 + 4 +print(3 + 4) # muestra: 7 +``` + +## Otras operaciones aritméticas -* `-` — resta -* `*` — multiplicación -* `**` — exponente -* `/` — división -* `//` — [división entera](https://es.wikipedia.org/wiki/División_euclídea) -* `%` — [resto de la división](https://es.wikipedia.org/wiki/División_euclídea) +Python admite todas las operaciones habituales más algunas específicas, relacionadas con cómo se guardan y procesan los números en el ordenador: -Ahora vamos a mostrar en pantalla el resultado de una división y luego el resultado de elevar un número a una potencia: +| Operación | Símbolo | Ejemplo | Resultado | +|-------------------------|---------|--------------|-----------| +| Suma | `+` | `2 + 3` | `5` | +| Resta | `-` | `7 - 2` | `5` | +| Multiplicación | `*` | `4 * 3` | `12` | +| División | `/` | `8 / 2` | `4.0` | +| Potenciación | `**` | `3 ** 2` | `9` | +| División entera | `//` | `7 // 3` | `2` | +| Resto de la división | `%` | `7 % 3` | `1` | + +Así se puede mostrar el resultado de una división y de una potenciación: + +```python +print(8 / 2) # => 4.0 +print(3 ** 2) # => 9 +``` + +## Números de punto flotante + +Además de los números enteros, en Python hay números de punto flotante, que se usan para trabajar con fracciones. Esos números se escriben con punto: ```python -print(8 / 2) # => 4.0 (Al dividir dos números, el tipo de dato resultante es float) -print(3 ** 2) # => 9 +print(3.5 + 1.2) # => 4.7 +print(10 / 4) # => 2.5 ``` -A veces, para mayor comodidad, mostraremos el resultado de ejecutar líneas de código de esta manera en los comentarios: `=> RESULTADO`. Por ejemplo, `# => 4`. +A veces los usamos nosotros mismos, cuando hay que trabajar precisamente con valores fraccionarios, por ejemplo al calcular una media o al trabajar con dinero y medidas. Pero los números de punto flotante pueden aparecer también por sí solos, por ejemplo como resultado de la operación de división `/`: + +```python +print(8 / 2) # => 4.0 +print(7 / 2) # => 3.5 +``` + +Aquí Python siempre devuelve un resultado fraccionario, incluso si matemáticamente la respuesta salió entera. + +La razón de separar esto en un tipo aparte: el ordenador necesita guardar los valores enteros y los fraccionarios de forma distinta. Para los enteros reserva unas estructuras en memoria, para los fraccionarios reserva otras. Por eso en Python, igual que en otros lenguajes de programación, existen dos clases distintas de números: int (enteros) y float (de punto flotante). + +En el nivel básico basta con recordar: los números enteros hacen falta cuando no hay fracciones, y los de punto flotante cuando sí las hay. Más adelante en el curso los veremos en detalle. + +## Qué es el resto de la división (`%`) + +Esta operación se llama **tomar el resto de la división**. Muestra **qué "queda"** cuando un número se divide por otro *no del todo*. Un ejemplo: + +```python +print(7 % 3) # => 1 +``` + +¿Por qué el resultado es igual a 1? + +- 7 se divide por 3 dos veces: 3 * 2 = 6 +- Hasta 7 queda 1, y eso es el resto. + +Otros ejemplos: + +```python +print(10 % 4) # => 2 (10 se divide por 4 dos veces: 4 * 2 = 8, resto 2) +print(15 % 5) # => 0 (se divide sin resto) +``` + +La operación % se usa a menudo en programación, por ejemplo: + +- para comprobar si un número se divide de forma exacta (si el resto es 0) +- para realizar acciones cíclicas, por ejemplo un comportamiento según índices pares o impares + +Nos encontraremos con % más de una vez en las tareas y veremos su uso en la práctica. + +## El formato de las expresiones aritméticas + +Desde el punto de vista de Python, entre `3+4` y `3 + 4` no hay diferencia. El intérprete entenderá las dos variantes igual y en ambos casos hará la suma. La diferencia está solo en el formato del código. En programación se acostumbra a poner espacios alrededor de los operadores aritméticos, porque así las expresiones son más fáciles de leer: + +```python +3 + 4 +8 / 2 +7 % 3 +``` + +La variante sin espacios también funciona: + +```python +3+4 +8/2 +7%3 +``` -La primera instrucción mostrará `4` en la pantalla (porque 8 / 2 es igual a 4), y la segunda instrucción mostrará 9 en la pantalla (porque 32 es igual a 9). +Pero ese código se ve menos cuidado y cuesta más percibirlo rápido. Por eso conviene acostumbrarse desde el principio a escribir con espacios alrededor de los operadores. diff --git a/modules/20-arithmetics/20-basic/es/assets/arithmetics.png b/modules/20-arithmetics/20-basic/es/assets/arithmetics.png new file mode 100644 index 00000000..ae492022 Binary files /dev/null and b/modules/20-arithmetics/20-basic/es/assets/arithmetics.png differ diff --git a/modules/20-arithmetics/25-operator/es/README.md b/modules/20-arithmetics/25-operator/es/README.md index f5edd1f1..8fd5cfdf 100644 --- a/modules/20-arithmetics/25-operator/es/README.md +++ b/modules/20-arithmetics/25-operator/es/README.md @@ -1,18 +1,67 @@ +En matemáticas y en programación usamos a menudo signos de operaciones, como `+`, `-`, `*` y otros. En programación esos signos se llaman operadores. -Un **operador** es un símbolo de operación, como `+`. Los operadores realizan operaciones en función de valores específicos, llamados **operandos**. Los operadores en sí suelen ser uno o varios caracteres, aunque en ocasiones pueden ser también una palabra. La gran mayoría de los operadores corresponden a operaciones matemáticas. +- Un operador es un símbolo o una palabra que denota una acción. +- Los operandos son los valores a los que se aplica el operador. + +Un ejemplo: ```python print(8 + 2) ``` -En este ejemplo, `+` es el operador y los números `8` y `2` son los **operandos**. +Aquí: + +- `+` es el operador +- `8` y `2` son los operandos +- el resultado será `10` + +```text +operando operador operando resultado + 8 + 2 → 10 + 5 - 3 → 2 + 4 * 3 → 12 +``` -Cuando sumamos, tenemos dos operandos: uno a la izquierda y otro a la derecha del signo `+`. Las operaciones que requieren dos operandos se llaman **binarias**. Si falta al menos un operando, por ejemplo, `3 +`, el programa dará un error de sintaxis. +## Operadores unarios -Las operaciones no sólo pueden ser binarias; también pueden ser unarias, con un solo operando, o ternarias, con tres operandos. Además, los operadores pueden tener la misma apariencia pero representar operaciones diferentes. Los símbolos `+` y `-` no solo se utilizan como operadores. Cuando se trata de números negativos, el signo menos forma parte del número: +Existen también las operaciones unarias, que trabajan con un solo operando. Un ejemplo: ```python print(-3) # => -3 ``` -En el ejemplo anterior, se aplica una operación unaria al número `3`. El operador menos antes del tres le indica al intérprete que tome el número `3` y encuentre su opuesto, es decir, `-3`. Esto puede resultar confuso, ya que `-3` es tanto un número en sí mismo como un operador con un operando. Sin embargo, esta es la estructura que tienen los lenguajes de programación. +En este caso `-` es un operador unario y `3` es el operando. El intérprete recibe la orden: «toma el número 3 y cambia su signo». + +El operador `-` se puede usar de distintas maneras. Cuando está **entre dos números**, es la operación de resta: + +```python +print(5 - 2) # => 3 +print(10 - 7) # => 3 +``` + +Aquí `-` toma el primer número y le resta el segundo. + +Esa diferencia se nota especialmente al trabajar con números negativos. Por ejemplo: + +```python +# menos por menos da más +print(5 - -2) # => 7 +``` + +Primero vemos la operación de resta: `5 - (...)`. Pero a la derecha está el menos unario `-2`, que convierte el `2` en un número negativo. Al final resulta: `5 - (-2) = 7`. + +Así, el significado de `-` depende del contexto: si al lado hay otro número, es una resta; si no, es un cambio de signo del número. + +Lo principal que hay que recordar aquí es que el comportamiento e incluso la propia escritura corresponden por completo a cómo lo hacíamos en la escuela. + +## Errores en los cálculos y en el análisis + +Si se percibe `-3` como un número único, se puede no notar que `-` es un operador aparte con su propia prioridad. Por ejemplo: + +```python +print(-3**2) +``` + +A primera vista puede parecer que se eleva al cuadrado `-3` y que el resultado debería ser `9` (cualquier número al cuadrado se vuelve positivo). Pero el resultado será `-9`. + +La cuestión está en el orden de los cálculos: primero se realiza la potenciación (`**`) y solo después se aplica el menos unario. Es decir, el programa calcula así: `-(3**2) = -9`. De la prioridad de las operaciones hablaremos en detalle más adelante en el curso. diff --git a/modules/20-arithmetics/27-commutativity/es/README.md b/modules/20-arithmetics/27-commutativity/es/README.md index 1951a5da..8967a16f 100644 --- a/modules/20-arithmetics/27-commutativity/es/README.md +++ b/modules/20-arithmetics/27-commutativity/es/README.md @@ -1,3 +1,57 @@ -"La suma no cambia al cambiar el orden de los sumandos" es una de las leyes básicas de la aritmética, también conocida como **ley conmutativa**. Una operación binaria se considera conmutativa si se obtiene el mismo resultado al intercambiar los operandos. En este caso, la suma es una operación conmutativa: `3 + 2 = 2 + 3`. +La frase «el orden de los sumandos no altera la suma» le resulta familiar a cualquiera desde la escuela. Ese principio se llama ley conmutativa y es una de las leyes básicas de la aritmética. -Sin embargo, la resta no es una operación conmutativa: `2 - 3 ≠ 3 - 2`. En programación, esta ley funciona de la misma manera que en aritmética. Además, la mayoría de las operaciones con las que nos encontramos en la vida real no son conmutativas. Por lo tanto, siempre preste atención al orden con el que estás trabajando. +## Qué es la conmutatividad + +Una operación se llama conmutativa si el orden de los operandos no influye en el resultado: intercambiando los valores obtendrás la misma respuesta. Un ejemplo de operación conmutativa: la suma. + +```python +print(3 + 2) # => 5 +print(2 + 3) # => 5 +``` + +El resultado idéntico confirma que la operación es conmutativa. + +```text +2 + 3 = 5 3 + 2 = 5 +└──────────┬─────────┘ + mismo resultado + +2 - 3 = -1 3 - 2 = 1 +└──────────┬─────────┘ + resultado distinto +``` + +## Operaciones no conmutativas + +Pero no todas las operaciones tienen esa propiedad. Por ejemplo, la resta es una operación no conmutativa: + +```python +print(2 - 3) # => -1 +print(3 - 2) # => 1 +``` + +Intercambiar los operandos da otro resultado. + +## En programación es exactamente igual + +La conmutatividad en programación funciona exactamente igual que en aritmética. Python sigue estrictamente las reglas matemáticas. + +Otras operaciones no conmutativas: + +- La división: _8 / 2 ≠ 2 / 8_ +- La potenciación: _2 **3 ≠ 3** 2_ + +Ejemplos en código: + +```python +# División +print(8 / 2) # 8 dividido entre 2 = 4.0 + +# Potenciación +print(3 ** 2) # 3 al cuadrado = 9 +``` + +Por eso: + +- Comprueba siempre con atención el orden de los operandos, especialmente al trabajar con operaciones desconocidas; +- comprueba la conmutatividad de forma experimental, en vez de suponerla de antemano. diff --git a/modules/20-arithmetics/30-composition/es/README.md b/modules/20-arithmetics/30-composition/es/README.md index ae814b6a..1c4b1bb8 100644 --- a/modules/20-arithmetics/30-composition/es/README.md +++ b/modules/20-arithmetics/30-composition/es/README.md @@ -1,13 +1,74 @@ +En Python, igual que en matemáticas, se pueden combinar varias operaciones en una línea. El intérprete procesa esas expresiones paso a paso, según reglas determinadas. + Veamos un ejemplo: ```python print(2 * 4 * 5 * 10) ``` -Las operaciones se pueden combinar entre sí y calcular expresiones compuestas más complejas. Para comprender cómo se realizan los cálculos dentro del intérprete, analicemos el ejemplo: `2 * 4 * 5 * 10`. +Este código consta de varias operaciones de multiplicación combinadas en una sola expresión. Para entender cómo el intérprete ejecuta la expresión, la analizaremos por etapas: + +- Primero se calcula `2 * 4`: `8 * 5 * 10` +- Después `8 * 5`: `40 * 10` +- Y por último `40 * 10`: `400` + +El resultado final es `400`. + +## ¿Y si las operaciones son distintas? + +Todo es simple mientras se usan los mismos operadores. Pero ¿qué ocurrirá si se combinan, por ejemplo, la multiplicación y la suma? + +```python +print(2 + 3 * 4) +``` + +```text +2 + 3 * 4 + └─┬─┘ +2 + 12 +└──┬───┘ + 14 +``` + +¿Saldrá `20` o `14`? La respuesta: `14`. + +Esto se explica porque en Python, igual que en matemáticas, las operaciones tienen prioridad. La multiplicación se realiza antes que la suma si no se usan paréntesis. Lo veremos en detalle en la lección sobre prioridades. + +## Ejemplos con la resta y con números negativos + +La misma regla funciona para la resta: + +```python +print(10 - 2 * 3) # => 4 +``` + +Primero se realiza la multiplicación: `10 - 6 = 4`. + +Y si en la expresión hay números negativos, el menos unario se aplica después de la potenciación: + +```python +print(-2 ** 2) # => -4, dos elevado a dos, después se aplica el menos +print(-2 * 5) # => -10, menos dos multiplicado por cinco +print(4 + -2) # => 2 +print(6 - -2) # => 8 +``` + +En todos los ejemplos, salvo el primero, primero se calcula el menos unario (`-2`) y después se realizan las demás operaciones. + +Veamos con más detalle el último ejemplo: + +```python +print(6 - -2) # => 8 +``` + +Primero se calcula el menos unario (`-2`), y entonces la operación se convierte en `6 - (-2)`, lo que da `8`. Es lo mismo que: + +```python +print(6 + 2) # => 8 +``` -1. Primero se calcula `2 * 4` y se obtiene la expresión `8 * 5 * 10` -2. Luego `8 * 5`. Al final, tenemos `40 * 10` -3. Finalmente, se realiza la última multiplicación y se obtiene el resultado `400` +## Qué hay que recordar -Las operaciones pueden contener diferentes operadores, lo que plantea la cuestión de su prioridad. +- Las expresiones pueden constar de varias operaciones. +- Python las calcula por etapas: de izquierda a derecha, respetando la prioridad de las operaciones. +- Los paréntesis se pueden usar para indicar de forma explícita el orden de los cálculos. diff --git a/modules/20-arithmetics/40-priority/es/README.md b/modules/20-arithmetics/40-priority/es/README.md index 6748edde..891dbfc2 100644 --- a/modules/20-arithmetics/40-priority/es/README.md +++ b/modules/20-arithmetics/40-priority/es/README.md @@ -1,21 +1,58 @@ -Supongamos que necesitamos calcular la siguiente expresión: `2 + 2 * 2`. Así es como se escribe: +Veamos una expresión simple: ```python print(2 + 2 * 2) # => 6 ``` -En las matemáticas escolares, existe el concepto de "prioridad de operación". La prioridad determina el orden en el que se deben realizar las operaciones. La multiplicación y la división tienen mayor prioridad que la suma y la resta, y la potenciación tiene la mayor prioridad de todas las operaciones aritméticas. Por ejemplo: `2 ** 3 * 2` se calculará como `16`. +El resultado es 6, no 8. Esto se explica por el concepto de prioridad de las operaciones en matemáticas y en programación. Determina el orden en que se realizan las acciones: -Sin embargo, a menudo los cálculos deben realizarse en un orden diferente al de la prioridad estándar. En ese caso, se deben utilizar paréntesis para establecer la prioridad. Así se hacía en la escuela, por ejemplo: `(2 + 2) * 2`. Los paréntesis se pueden colocar alrededor de cualquier operación y pueden anidarse tantas veces como sea necesario. Aquí tienes algunos ejemplos: +- La multiplicación y la división se realizan antes que la suma y la resta. +- La potenciación (**) tiene una prioridad aún más alta. + +```text +Prioridad de las operaciones (de alta a baja): + + ** potenciación + ↓ + * / // % multiplicación, división, resto + ↓ + + - suma, resta +``` + +Por ejemplo: + +```python +print(2 * 2 ** 3) # => 16, porque primero 2 ** 3 = 8, después 8 * 2 = 16 +``` + +Si van seguidas operaciones con la misma prioridad, se realizan de izquierda a derecha: + +```python +print(8 / 2 * 3) # => 12, porque primero 8 / 2 = 4, después 4 * 3 = 12 +``` + +## Controlar el orden de las acciones + +A veces hay que cambiar el orden de los cálculos. Para eso se usan los paréntesis. Permiten indicar qué acciones hay que realizar en primer lugar: + +```python +print((2 + 2) * 2) # => 8 +``` + +Los paréntesis se pueden poner alrededor de cualquier parte de la expresión y anidarlos unos dentro de otros: ```python print(3 ** (4 - 2)) # => 9 print(7 * 3 + (4 / 2) - (8 + (2 - 1))) # => 14 ``` -Lo más importante es asegurarse de que los paréntesis estén correctamente emparejados. Esto, a menudo, es la causa de errores no sólo para principiantes, sino también para programadores experimentados. Para mayor comodidad, coloca el paréntesis de apertura y cierre de inmediato, y luego escribe la parte interna. El editor en nuestro sitio web (y la mayoría de los otros editores de código) lo hacen automáticamente: escribes `(` y el editor agrega automáticamente `)`. Esto también se aplica a otros caracteres emparejados, como comillas. Hablaremos de ellos en futuras lecciones. +La regla principal: cierra siempre los paréntesis. Los paréntesis sin pareja provocan errores: tanto los principiantes como los programadores con experiencia se olvidan a veces del paréntesis de cierre. + +> Escribe los paréntesis de inmediato en pareja. Por ejemplo, teclea () y después rellena la parte de dentro. La mayoría de los editores de código (incluido el nuestro) añaden automáticamente el paréntesis de cierre en cuanto escribes el de apertura. + +## Mejoramos la legibilidad -A veces, una expresión puede ser difícil de entender visualmente. En ese caso, se pueden colocar paréntesis sin afectar la prioridad: +A veces una expresión funciona correctamente, pero se ve enredada. En esos casos los paréntesis se pueden añadir simplemente por claridad: no cambiarán el resultado, pero mejorarán la percepción. ```python # Antes @@ -25,4 +62,4 @@ print(8 / 2 + 5 - -3 / 2) # => 10.5 print(((8 / 2) + 5) - (-3 / 2)) # => 10.5 ``` -Es importante recordar que el código se escribe para ser entendido por personas. Las personas leerán el código y las máquinas solamente lo ejecutarán. Para las máquinas, el código es correcto o incorrecto. No hay código "más" o "menos" comprensible para ellas. +Los programas los escriben personas, y también los leen personas. Al ordenador le da igual lo claro que esté escrito el código: basta con que sea sintácticamente correcto. Para una persona, un código claro y cuidado es garantía de comodidad, especialmente al trabajar en equipo o al analizar errores. diff --git a/modules/20-arithmetics/43-float/es/README.md b/modules/20-arithmetics/43-float/es/README.md index be1dabde..2d21b6e4 100644 --- a/modules/20-arithmetics/43-float/es/README.md +++ b/modules/20-arithmetics/43-float/es/README.md @@ -1,12 +1,50 @@ +En matemáticas hay distintos tipos de números. Por ejemplo: -En matemáticas existen diferentes tipos de números, por ejemplo, los números naturales son enteros mayores o iguales a uno, o los números racionales son números con decimales, como 0.5. Desde el punto de vista de los dispositivos informáticos, hay una brecha entre estos tipos de números. Intenta responder a la siguiente pregunta, ¿cuánto es *0.2 + 0.1*? Y ahora veamos qué dice Python al respecto: +- Naturales: números enteros positivos: 1, 2, 3, etc. +- Racionales: números fraccionarios que se pueden representar como una división, por ejemplo: 0.5, 1.75, 3.14. + +Desde el punto de vista de las matemáticas todo es simple. Pero desde el punto de vista del ordenador, entre esos tipos de números hay un auténtico abismo. Intenta resolver mentalmente: ¿cuánto es `0.2` + `0.1`? Evidentemente, `0.3`. Y esto es lo que dice Python: ```python -0.2 + 0.1 # 0.30000000000000004 +print(0.2 + 0.1) # => 0.30000000000000004 ``` -La operación de suma de dos números racionales resultó en un cálculo inexacto del resultado. Otros lenguajes de programación también darán el mismo resultado. Este comportamiento se debe a las limitaciones de la capacidad de cálculo. A diferencia de los números, la cantidad de memoria es finita (un número infinito de números requiere una cantidad infinita de memoria para almacenarlos). +En lugar del habitual 0.3 obtenemos 0.30000000000000004. + +```text +Expectativa: 0.1 + 0.2 → 0.3 +Realidad: 0.1 + 0.2 → 0.30000000000000004 + └── error de almacenamiento +``` + +## ¿Por qué ocurre esto? + +Este comportamiento es propio de JavaScript, C++ y la mayoría de los demás lenguajes de programación. + +La causa está en la construcción del ordenador. El ordenador trabaja con una memoria limitada, mientras que los números racionales son infinitamente precisos. Entre 0.1 y 0.2 se pueden colocar infinitos otros números. Pero el ordenador no puede almacenar el infinito. Aproxima el número, intentando encajarlo en la cantidad de bits disponible. + +Esos valores aproximados se llaman números de punto flotante (floating point numbers). Su almacenamiento y los cálculos con ellos obedecen a reglas estrictas, descritas en el estándar especial IEEE 754, al que se ajustan la mayoría de los lenguajes de programación. + +## Cuándo aparecen esos números + +Los números de punto flotante aparecen en los programas más a menudo de lo que puede parecer. Estos son los casos principales: + +- Cuando escribes de forma explícita un número fraccionario, por ejemplo 0.1, 2.5, 3.14. +- Cuando realizas una división, incluso si divides dos números enteros: + +```python +print(1 / 2) # => 0.5 +print(2 / 3) # => 0.6666666666666666 +``` + +Aunque el resultado parezca «bonito», por dentro está representado igualmente como un valor aproximado. Algunas fracciones, como 1 / 3, no se pueden representar de forma exacta en el sistema binario, por eso su precisión siempre está limitada. + +## Dónde es crítico y cómo se trabaja con ello + +Normalmente un error pequeño no molesta. Pero en los cálculos financieros, en las tareas científicas y de ingeniería, y también en la comparación exacta de resultados, puede convertirse en un problema. Por ejemplo, un error de una fracción de céntimo es capaz de dar un total incorrecto, y una cadena larga de cálculos puede acumular la imprecisión poco a poco. + +En los programas reales esto se maneja de distintas maneras. El dinero se guarda a menudo en las unidades mínimas, por ejemplo en céntimos, es decir, se usan números enteros en lugar de fraccionarios. En otros casos el resultado se redondea a la cantidad de decimales necesaria, se comparan los números con un error admisible o se usan tipos de datos y bibliotecas especiales para cálculos exactos. -Los números racionales no están dispuestos en una secuencia continua, entre *0.1* y *0.2* hay un conjunto infinito de números. Por lo tanto, surge un problema importante: ¿cómo almacenar números racionales? Esta es una pregunta interesante en sí misma. En Internet hay muchos artículos dedicados a la organización de la memoria en estos casos. Además, existe un estándar que describe cómo hacerlo correctamente, y la mayoría de los lenguajes se basan en él. +## Qué hay que recordar -Para nosotros, como desarrolladores, es importante entender que las operaciones con números de punto flotante no son precisas (se puede ajustar esta precisión), por lo que al resolver problemas relacionados con este tipo de números, es necesario recurrir a trucos especiales que permitan lograr la precisión necesaria. +Las operaciones con números de punto flotante no siempre son exactas, y eso es normal. Ese comportamiento es propio de la mayoría de los lenguajes de programación y se explica por la construcción de la memoria del ordenador. La precisión se puede controlar, por ejemplo mediante el redondeo o comparando los números con un error dado. Y al trabajar con dinero, con medidas exactas o con cálculos científicos, es mejor usar tipos de datos especiales que garanticen el control sobre la precisión. diff --git a/modules/20-arithmetics/45-linting/es/README.md b/modules/20-arithmetics/45-linting/es/README.md index 1185651d..c724be68 100644 --- a/modules/20-arithmetics/45-linting/es/README.md +++ b/modules/20-arithmetics/45-linting/es/README.md @@ -1,17 +1,60 @@ -Ya hemos aprendido a escribir programas simples, por lo que ahora podemos hablar un poco sobre cómo escribirlos correctamente. +Cuando distintos desarrolladores escriben código con estilos distintos, el código se vuelve difícil de leer: en un sitio hay un espacio de más, en otro las sangrías son diferentes. Para evitar discrepancias, los programadores acordaron respetar un estilo de codificación único. Ese conjunto de reglas describe cómo debe verse el código: la colocación de los espacios, el formato de las funciones y los nombres de las variables. -El código debe ser formateado de una manera específica para que sea comprensible y fácil de mantener en el tiempo. Existen conjuntos de reglas especiales que describen varios aspectos de la escritura de código, llamadas **convenciones de codificación**. En Python, hay una convención estándar: [PEP8](https://peps.python.org/pep-0008/). Este documento estándar responde prácticamente todas las preguntas sobre cómo formatear una parte específica del código. Este documento contiene todas las reglas que se deben seguir. Recomendamos a los principiantes que se acostumbren a consultar el estándar PEP8 y escribir cualquier código siguiendo sus pautas. +Un estilo único significa código igualmente claro para todos los miembros del equipo, independientemente de quién lo haya escrito. Eso ahorra tiempo, reduce la cantidad de errores y simplifica el trabajo conjunto. -Hoy en día no es necesario recordar todas las reglas de la convención estándar, ya que existen programas especiales que verifican automáticamente el código y señalan las violaciones. Estos programas se llaman **linters**. Verifican el código para asegurarse del cumplimiento de los estándares. En Python, hay muchos linters, y el más popular de ellos es [flake8](https://flake8.pycqa.org/en/latest/). +## Estándares de codificación -Echemos un vistazo a un ejemplo: +En el lenguaje Python hay un estilo de codificación oficial: el documento PEP8. Describe en detalle cómo dar formato al código: qué sangrías usar, cómo colocar los espacios, de qué longitud deben ser las líneas, cómo nombrar las variables y mucho más. + +Ese estándar lo conocen y lo usan todos los desarrolladores de Python. A los principiantes les resulta útil consultarlo de vez en cuando y adquirir los hábitos correctos desde el principio. Sin embargo, no hace falta memorizarlo todo de golpe. + +## Linters: comprobación automática del código + +No hace falta memorizar todas las reglas a mano. Existen programas especiales que lo hacen por ti. Se llaman linters. + +Un linter es una herramienta que analiza tu código e informa de las violaciones de los estándares. +Ayuda a: + +- Deshacerse de los espacios de más +- Respetar las sangrías +- Escribir expresiones legibles y elegantes + +## Un linter moderno: Ruff + +A día de hoy, el linter más rápido y popular del mundo Python se considera Ruff. Reúne en sí las reglas de muchas otras herramientas: flake8, isort, pylint, black y otras. Ruff funciona rápido, admite la sintaxis moderna y se desarrolla activamente. + +Veamos un ejemplo: ```python result = 1+ 3 ``` -El linter mostrará un error por violar la regla: *E225 missing whitespace around operator*. Según el estándar, el operador `+` siempre debe estar separado por espacios de los operandos. +Ese código se ve descuidado, y el linter señalará el error con razón. Así se ve el proceso de comprobación: + +```text +Código Linter Resultado +┌──────────┐ ┌──────────┐ ┌─────────────────┐ +│ result= │ → │ Ruff │ → │ E225: missing │ +│ 1+ 3 │ │ │ │ whitespace │ +└──────────┘ └──────────┘ └─────────────────┘ +``` + +```text +E225: missing whitespace around operator +``` + +Eso significa que antes y después del + faltan espacios. Según el estándar, debería ser así: + +```python +result = 1 + 3 +``` + +## Las reglas y su sentido + +Cada mensaje del linter está ligado a una regla concreta. Por ejemplo, E225 se refiere a los espacios, E302 se refiere a las líneas vacías antes de las funciones, y E501 se refiere a la longitud de las líneas. Cuando acabas de empezar, esos detalles pueden parecer poco importantes. Pero con el tiempo queda claro que son precisamente ellos los que forman un estilo único y legible. + +La lista completa de reglas de Ruff se puede consultar en la [documentación oficial](https://docs.astral.sh/ruff/rules/). -En el ejemplo anterior, vimos la regla [E225](https://pep8.readthedocs.io/en/release-1.7.x/intro.html#error-codes), que es apenas una sola de la larga lista de reglas. Otras reglas describen la indentación, los nombres, los paréntesis, las operaciones matemáticas, la longitud de las líneas y muchos otros aspectos. Cada regla individual puede parecer insignificante y pequeña, pero juntas forman la base de un buen código. La lista completa de reglas de flake8 está disponible en [esta documentación](https://flake8.pycqa.org/en/latest/user/error-codes.html). +## Usar el linter en tus propios proyectos -Ya estás familiarizado con el linter, ya que la plataforma de Hexlet verifica tu código utilizando uno. Pronto comenzarás a usarlo fuera de Hexlet, cuando implementes proyectos educativos. Configurarás el linter y verificará el código en tiempo real, informándote sobre las violaciones. +Cuando empieces a escribir tus propios proyectos fuera de la plataforma educativa, el linter será un ayudante imprescindible. Se puede configurar en cualquier editor de código, ejecutar en la terminal o conectar a la construcción del proyecto. El linter muestra los errores y sabe corregirlos automáticamente. diff --git a/modules/25-strings/10-quotes/es/README.md b/modules/25-strings/10-quotes/es/README.md index 08f3b5d3..6379b6be 100644 --- a/modules/25-strings/10-quotes/es/README.md +++ b/modules/25-strings/10-quotes/es/README.md @@ -1,7 +1,8 @@ +Las cadenas en la programación en Python se usan con mucha frecuencia y en las situaciones más diversas. Con ellas trabajamos con texto, mostramos mensajes en la pantalla, procesamos la entrada del usuario e interactuamos con sistemas externos. -En esta lección vamos a entender qué es una cadena de texto y qué papel juegan las comillas en el código. +![Cadenas de texto](./assets/strings.png) -Definir una cadena de texto es bastante fácil: es un conjunto de caracteres. Imaginemos que tenemos las siguientes instrucciones: +Desde el punto de vista de Python, una cadena es simplemente un conjunto de caracteres encerrado entre comillas. Veamos unos ejemplos. ```python 'Hello' @@ -11,59 +12,103 @@ Definir una cadena de texto es bastante fácil: es un conjunto de caracteres. Im '' ``` -¿Cuáles de estas opciones son cadenas de texto? En realidad, todas las cinco opciones previas son válidas: +Todas esas variantes son cadenas. -* Con `'Hello'` y `'Goodbye'` es bastante obvio el asunto, ya que previamente hemos trabajado con construcciones similares y las hemos llamado cadenas de texto. -* `'G'` y `' '` también son cadenas de texto; simplemente contienen un solo carácter. -* `''` es una cadena de texto vacía, ya que no contiene ningún carácter. +- `'Hello'`, `'Goodbye'` y `'G'` son cadenas de varios caracteres o de uno solo. +- `' '` es una cadena formada por un solo espacio. +- `''` es una cadena vacía, en ella no hay ni un carácter. Cumple el mismo papel que el 0 en matemáticas. -Consideramos como cadena de texto todo lo que está dentro de comillas, ya sea si se trata de un espacio, un solo carácter, o incluso, la ausencia de caracteres. +Es decir, todo lo que está dentro de las comillas se considera una cadena, aunque solo haya un espacio o no haya absolutamente nada. -Hasta ahora hemos estado escribiendo cadenas de texto entre comillas simples, pero esa no es la única forma. También podemos usar comillas dobles: +Si se muestran las cadenas en la pantalla, `'Hello'` y `'Goodbye'` se verán con claridad. Pero `' '` y `''` pueden despistar, porque la salida de una cadena vacía parece una ausencia total, mientras que una cadena con un espacio muestra un «espacio vacío» que visualmente es difícil de distinguir. Sin embargo, Python las diferencia con claridad. Una cadena vacía significa la ausencia de caracteres, mientras que una cadena con un espacio contiene un carácter de espacio concreto. + +Pregunta de control. ¿Son estas cadenas iguales o no? + +```python +'hexlet' +' hexlet' +``` + +## Terminología. ¿Cadena o línea? + +En programación hay una trampa terminológica. + +- Una cadena (string) es un tipo de dato (el que analizamos arriba), por ejemplo 'hello'. +- Una línea (line) es una fila de texto en un archivo o en el código. + +Por ejemplo, en el código de abajo hay una línea, pero no una cadena. + +```python +print(5) +``` + +Para evitar la confusión, en este curso usaremos estas formulaciones. + +- Cadena, cuando hablamos del tipo de dato. +- Línea, cuando se trata de las filas del código. + +## Comillas simples y dobles + +En Python las cadenas se pueden escribir tanto entre comillas simples como entre dobles. ```python -print("Dracarys!") +print('Hello') +print("Hello") ``` -Ahora, imaginemos que queremos imprimir la frase *La madre de los dragones*. El apóstrofe antes de la letra **s** es un carácter igual que una comilla simple. Intentemos esto: +Por defecto se acostumbra a usar comillas simples `'`, si dentro de la cadena no se necesitan dobles. Ese estilo lo sigue el estándar oficial de formato de código *PEP8*. - ```python +## El problema de las comillas dentro de la cadena + +Imagina que quieres imprimir la cadena *Dragon's mother*. En ella hay un apóstrofo (*'s*) que coincide con el carácter de comilla simple. Probemos así. + +```python print('Dragon's mother') # SyntaxError: invalid syntax ``` -Este programa no funcionará. Desde el punto de vista de Python, la cadena de texto comenzó con una comilla simple y terminó después de la palabra **dragon**. Luego hubo caracteres `s madre` sin comillas, lo que significa que no es una cadena de texto. Y luego hubo una comilla de apertura de cadena que nunca se cerró: `')`. Este código contiene un error de sintaxis, incluso se puede ver por cómo se resalta el código. - -Para evitar este error, usamos comillas dobles. Este programa funcionará correctamente: +Python decidirá que la cadena termina después de la palabra 'Dragon', y el resto no lo reconocerá como código válido, lo que provocará un error de sintaxis. Para evitarlo, envolvemos la cadena en comillas dobles. ```python print("Dragon's mother") ``` -Ahora el intérprete sabe que la cadena de texto comenzó con una comilla doble y también debe terminar con una comilla doble. Y la comilla simple dentro se convirtió en parte de la cadena de texto. +Ahora Python entiende que la comilla simple dentro de la cadena es un carácter normal, y que la cadena en sí empieza y termina con comillas dobles. -Lo contrario también es cierto. Si queremos usar comillas dobles dentro de una cadena de texto, debemos hacer la cadena de texto con comillas simples. Y la cantidad de comillas dentro de la cadena de texto no importa. +Si dentro de la cadena hacen falta comillas dobles y por fuera simples, tampoco habrá problemas. -Ahora imaginemos que queremos crear la siguiente cadena de texto: +```python +print('He said "No"') +``` + +A veces en la cadena aparecen los dos tipos de comillas. ```python Dragon's mother said "No" ``` -En ella hay comillas simples y comillas dobles. Necesitamos indicarle al intérprete de alguna manera que las comillas son uno de los caracteres dentro de la cadena de texto, no el comienzo o el final de la cadena de texto. - -Para esto se utiliza el **carácter de escape**: `\` - barra invertida. Si colocamos `\` antes de una comilla (simple o doble), el intérprete reconocerá la comilla como un carácter normal dentro de la cadena de texto, no como el comienzo o el final de la cadena de texto: +En ese caso, para que Python no confunda las comillas de dentro de la cadena con las exteriores, se usa el carácter de escape, la barra invertida `\`. Le dice al intérprete que el carácter que la sigue es parte de la cadena y no un carácter de control. ```python -# Escapamos las comillas alrededor de No para que el intérprete -# las reconozca como parte de la cadena de texto print("Dragon's mother said \"No\"") # => Dragon's mother said "No" ``` -Observa que en el ejemplo anterior no tuvimos que escapar la comilla simple (apóstrofe 's), porque la cadena de texto en sí se creó con comillas dobles. Si la cadena de texto se hubiera creado con comillas simples, entonces el carácter de escape sería necesario antes del apóstrofe, pero no antes de las comillas dobles. +Aquí escapamos las comillas dobles dentro de la cadena encerrada en comillas dobles. + +Fíjate: Python percibe `\"` como un solo carácter de comilla, no como dos caracteres. +Lo mismo ocurre con `\'`, `\\`, `\n` y otras secuencias de control. Se ven como dos caracteres en el código, pero en la cadena cuentan como uno. + +Lo mismo funciona en el caso contrario. + +```python +print('Dragon\'s mother said "No"') +# => Dragon's mother said "No" +``` + +## Cómo mostrar la barra invertida -Si queremos imprimir la barra invertida en sí, se aplica la misma regla. Como cualquier otro carácter especial, debe ser escapado, en el sentido de programación: +Para mostrar la barra invertida en sí, también hay que escaparla. ```python print("\\") diff --git a/modules/25-strings/10-quotes/es/assets/strings.png b/modules/25-strings/10-quotes/es/assets/strings.png new file mode 100644 index 00000000..4cce3286 Binary files /dev/null and b/modules/25-strings/10-quotes/es/assets/strings.png differ diff --git a/modules/25-strings/15-escape-characters/es/README.md b/modules/25-strings/15-escape-characters/es/README.md index e9add763..83394437 100644 --- a/modules/25-strings/15-escape-characters/es/README.md +++ b/modules/25-strings/15-escape-characters/es/README.md @@ -1,90 +1,120 @@ - -Queremos mostrar el siguiente diálogo: +Supongamos que queremos mostrar el texto de abajo en dos líneas. ```text - Are you hungry? - Aaaarrrgh! ``` -Intentemos imprimir en pantalla una cadena de texto con este contenido: +Si simplemente pasamos ese texto a `print()`, Python lo imprimirá todo en una sola línea. Técnicamente se pueden escribir dos `print()` seguidos, pero imaginemos que queremos hacerlo con uno solo. - ```python +```python print("- Are you hungry?- Aaaarrrgh!") # => - Are you hungry?- Aaaarrrgh! ``` -Como puedes ver, el resultado no es el que esperábamos. Las cadenas de texto se han colocado una al lado de la otra en lugar de una debajo de la otra. Necesitamos decirle al intérprete "presiona Enter" - y que haga un salto de línea después del signo de interrogación. Esto se puede hacer utilizando el carácter `\n`: +Para que cada una empiece en una línea nueva, hay que añadir un salto de línea, es decir, «pulsar Enter». En programación eso se implementa añadiendo caracteres especiales, en este caso `\n`. Sí, no es un error tipográfico. A pesar de que aquí vemos dos caracteres, desde el punto de vista de Python es un solo carácter. ```python print("- Are you hungry?\n- Aaaarrrgh!") -# => - Are you hungry? -# => - Aaaarrrgh! ``` -`\n` es un ejemplo de una **secuencia de escape**. Estas secuencias también se conocen como construcciones de control. No se pueden ver en la forma en que se escriben. +El resultado será este. -Cuando escribes algún texto en Word, presionas Enter al final de la línea. El editor agrega un carácter especial invisible al final de la línea, que se llama LINE FEED (LF, salto de línea). En algunos editores incluso puedes habilitar la visualización de caracteres invisibles. Entonces el texto se verá más o menos así: +```text +- Are you hungry? +- Aaaarrrgh! +``` + +## ¿Qué es `\n`? + +`\n` es una secuencia de control (en inglés escape sequence, a veces se dice «secuencia escapada»). Denota un salto de línea, pero no se muestra directamente. No verás `\n` en la salida del programa, ya que solo influye en la disposición del texto. + +En los editores de texto, al pulsar Enter se añade el carácter invisible LF (Line Feed). Eso es justamente lo que significa `\n`. A veces se pueden ver esos caracteres si se activa la visualización de caracteres especiales. ```text - ¡Hola!¶ -- ¡Oh, hola!¶ +- ¡Ah, hola!¶ - ¿Cómo estás? ``` -El dispositivo que muestra el texto correspondiente tiene en cuenta este carácter. Por ejemplo, una impresora, al encontrar un LF, avanza el papel hacia arriba una línea, y un editor de texto mueve todo el texto siguiente hacia abajo, también una línea. +Las impresoras, los editores y los intérpretes de Python entienden `\n` como la orden de empezar el texto en una línea nueva. -Hay varias docenas de estos caracteres invisibles, pero en programación sólo se encuentran algunos. Además del salto de línea, estos caracteres incluyen: +## Ejemplos de uso de `\n` -* tabulación `\t` - la ruptura que se produce al presionar la tecla Tab -* retorno de carro `\r` - solo funciona en Windows +Así procesa Python la secuencia de control `\n`. -Puedes reconocer estas construcciones de control en el texto por el carácter `\`. Los programadores a menudo usan el salto de línea `\n` para formatear correctamente el texto. Por ejemplo, escribamos este código: +```text +En el código 'Hello\nWorld' + ↓ +En pantalla Hello + World +``` + +La posición de `\n` cambia la salida final. ```python -print("Gregor Clegane\nDunsen\nPolliver\nChiswyck") +print("Hello\nWorld") +# Hello +# World + +print("Hello \nWorld") +# Hello +# World (al final de la primera línea hay un espacio) + +print("Hello\n World") +# Hello +# World (en la segunda línea hay un espacio al principio) + +print("Hello\n\nWorld") +# Hello +# +# World (una línea vacía entre ellas) ``` -Entonces se mostrará en pantalla: +Los espacios antes o después de `\n` también se cuentan. Python los percibe como caracteres normales y los muestra en la salida. -```text -Gregor Clegane -Dunsen -Polliver -Chiswyck +También puedes insertar `\n` en cualquier parte de la cadena: antes, después o incluso usarlo por separado. + +```python +print("First line") +print("\n") # Simplemente una línea vacía +print("Second line") ``` -Al trabajar con el carácter de salto de línea, ten en cuenta los siguientes puntos: +El resultado será este. -1. No importa lo que haya antes o después de `\n`: un carácter o una cadena vacía. El salto se detectará y se realizará de todos modos. +```text +First line -2. Una cadena puede contener solo `\n`: +Second line +``` - ```python -print('Gregor Clegane') # Cadena de texto -print("\n") # Cadena con un carácter de salto de línea invisible -print('Dunsen') # Cadena de texto - ``` +## Cómo mostrar el propio carácter `\n` - El programa mostrará en pantalla: +`\n` en Python es una secuencia de control. Controla la disposición del texto y no se muestra en la pantalla como los caracteres normales. Si necesitas mostrar precisamente los caracteres `\` y `n`, y no un salto de línea, hay que escaparlos. Para eso, antes de la barra invertida se añade otra barra. - ```text -Gregor Clegane +```python +print("Hello\\nWorld") +# Hello\nWorld +# Si se olvida indicar la segunda barra +print("Hello\nWorld") +# Hello +# World -Dunsen - ``` +``` -3. En el código, la secuencia `\n` se ve como dos caracteres, pero para el intérprete es un solo carácter especial. +En ese caso Python entiende `\\` como una barra invertida normal y muestra la cadena sin salto de línea. -4. Si deseas mostrar `\n` como texto (dos caracteres separados), puedes usar la técnica de escape: agregar otro `\` al principio. La secuencia `\\n` se mostrará como los caracteres `\` y `n`, que van uno tras otro: +## Otras secuencias de control -```python -print("Joffrey loves using \\n") -# => Joffrey loves using \n -``` +Además de `\n`, en Python hay otras secuencias de control. -En Windows, por defecto se utiliza `\r\n` para los saltos de línea. Esta combinación funciona bien solo en Windows, pero causa problemas al transferir a otros sistemas. Por ejemplo, cuando en un equipo de desarrollo hay usuarios de Linux. +- `\t` denota la tabulación (el equivalente de la tecla Tab). +- `\r` denota el retorno de carro (se usa en Windows, pero se aplica raramente en código Python). +- En programación se usa con más frecuencia precisamente `\n`, y basta para la mayoría de las tareas. -El problema es que la secuencia `\r\n` se interpreta de manera diferente según la codificación seleccionada, de lo cual hablaremos más adelante. Por esta razón, en entornos de desarrollo se recomienda siempre usar `\n` sin `\r`. +## Detalles importantes -En este caso, el salto de línea siempre se interpreta de la misma manera y funciona perfectamente en cualquier sistema. No olvides configurar tu editor para usar `\n`. +- `\n` es un solo carácter, a pesar de que en el código se escribe como dos (\ y n). +- En Windows se usa por defecto la combinación `\r\n`, pero en Python (y en general en el desarrollo multiplataforma) se acostumbra a usar solo `\n`, para evitar problemas al llevar el código entre sistemas. diff --git a/modules/25-strings/20-string-concatenation/es/README.md b/modules/25-strings/20-string-concatenation/es/README.md index b49a59bf..98cee066 100644 --- a/modules/25-strings/20-string-concatenation/es/README.md +++ b/modules/25-strings/20-string-concatenation/es/README.md @@ -1,30 +1,75 @@ +A menudo hay que construir cadenas a partir de varias partes, por ejemplo unir el nombre y el apellido, añadir una unidad de medida o componer un texto a partir de una plantilla. Para eso se usa la operación de concatenación, es decir, el pegado de cadenas. -En el desarrollo web, los programas operan constantemente con cadenas de texto. Todo lo que vemos en los sitios web está representado de alguna manera como texto. Este texto, en su mayoría, es dinámico, es decir, se obtiene a partir de diferentes partes que se unen. +## Cómo unir cadenas -Para unir cadenas de texto, es necesario realizar una **concatenación**: +En Python las cadenas se unen mediante el operador +. A pesar de que ese operador se usa también para sumar números, en el caso de las cadenas significa unión, es decir, pegar el contenido. ```python -# El operador es el mismo que se utiliza para sumar números, -# pero aquí tiene un significado diferente (semántica) -print('Dragon' + 'stone') # => Dragonstone +print('Dragon' + 'stone') +# => Dragonstone ``` -La concatenación de cadenas siempre se realiza en el mismo orden en el que se escriben los operandos. El operando izquierdo se convierte en la parte izquierda de la cadena, y el operando derecho en la parte derecha. Aquí hay algunos ejemplos adicionales: +El orden importa. Primero va la parte izquierda ('Dragon'), después la derecha ('stone'). El resultado sale en el orden en que están indicados los operandos. + +Así funciona la unión de varias cadenas. El código: + +```python +print('Hello' + ', ' + 'World!') +``` + +La ejecución: + +```text +'Hello' + ', ' + 'World!' +└──┬──┘ └┬┘ └──┬───┘ + └────┬───┘ │ + 'Hello, ' + 'World!' + └──────┬───────┘ + 'Hello, World!' +``` + +Ejemplos. ```python -print('Kings' + 'wood') # => Kingswood -print('Kings' + 'road') # => Kingsroad -print("King's" + 'Landing') # => King'sLanding +print('Kings' + 'wood') # => Kingswood +print('Kings' + 'road') # => Kingsroad +# Aquí por fuera hay comillas dobles, porque dentro hay una simple +print("King's" + 'Landing') # => King'sLanding ``` -Como puedes ver, puedes concatenar cadenas incluso si se escriben con diferentes tipos de comillas. +Python permite unir cadenas incluso si están escritas con comillas distintas. Lo importante es que las dos partes sean cadenas. -Un espacio en blanco es un carácter igual que los demás, por lo que la cantidad de espacios en blanco que coloques en una cadena será la misma en la cadena resultante: +## El espacio también es un carácter + +Al unir, Python no inserta espacios automáticamente. Si entre las partes debe haber un espacio, hay que indicarlo a mano. ```python -# Colocamos un espacio en blanco en la parte izquierda +# Espacio al final de la primera cadena print("King's " + 'Landing') # => King's Landing -# Colocamos un espacio en blanco en la parte derecha +# Espacio al principio de la segunda cadena print("King's" + ' Landing') # => King's Landing ``` + +El resultado será el mismo. Pero si no se añade el espacio, las palabras se pegarán. + +## Secuencias de control + +En las cadenas se pueden usar secuencias de control, por ejemplo `\n` para el salto de línea o `\t` para la tabulación. En la concatenación funcionan igual que cualquier otro carácter. + +```python +print('Hello,' + '\n' + 'World!') +# => +# Hello, +# World! + +print('A' + '\t' + 'B') +# => A B +``` + +## Conclusión + +La concatenación es la unión de cadenas mediante `+`, y las cadenas se pueden unir independientemente del tipo de comillas. + +- El pegado ocurre estrictamente en orden de izquierda a derecha. +- Los espacios no se añaden automáticamente, hay que incluirlos en las cadenas a mano. diff --git a/modules/25-strings/30-encoding/es/README.md b/modules/25-strings/30-encoding/es/README.md index f0677482..63fb2816 100644 --- a/modules/25-strings/30-encoding/es/README.md +++ b/modules/25-strings/30-encoding/es/README.md @@ -1,37 +1,83 @@ +En el nivel más básico, el ordenador trabaja solo con ceros y unos, lo que constituye el llamado código binario. Cada uno o cero se llama bit (de binary digit, «dígito binario»). -En su nivel más profundo, una computadora opera exclusivamente con los dígitos `0` y `1`. Esto se conoce como [código binario](https://es.wikipedia.org/wiki/C%C3%B3digo_binario), y los unos y ceros se llaman bits, abreviatura de "binary digit" (dígito binario). +Cualquier dato en el ordenador está representado simplemente como una secuencia de bits: por ejemplo las imágenes, la música y el texto. Los números del sistema decimal que nos son habituales también se pueden representar en forma binaria. -Los números que estamos acostumbrados a utilizar en el sistema decimal se codifican utilizando números binarios: +- 0 → `0` +- 1 → `1` +- 2 → `10` -- 0 ← 0 -- 1 ← 1 -- 2 ← 10 -- 3 ← 11 -- 4 ← 100 -- 5 ← 101 +## ¿Cómo codificar texto? -Pero, ¿qué pasa con el texto? En realidad, la computadora no sabe nada acerca de las letras, signos de puntuación y otros caracteres de texto. Todos estos caracteres también se codifican con números. +El ordenador no «entiende» el texto. Para trabajar con las letras y otros caracteres, también hay que convertirlos en números. Eso se hace mediante codificaciones, es decir, tablas en las que a cada carácter le corresponde un número determinado. -Podemos tomar el alfabeto inglés y asignar un número a cada letra, comenzando desde uno en orden: +La forma más simple consiste en numerar las letras empezando por 1. -- a ← 1 -- b ← 2 -- c ← 3 -- d ← 4 -- ... -- z ← 26 +- `a` → `1` +- `b` → `2` +- ...y así hasta `z` → `26` -Esto es lo que se conoce como **codificación**. +Ahora podemos representar la palabra hello como un conjunto de números. -Durante el proceso de funcionamiento, los programas utilizan codificaciones para convertir números en caracteres y viceversa. Sin embargo, el programa en sí no tiene conocimiento del significado de estos caracteres. +```text +h e l l o +↓ ↓ ↓ ↓ ↓ +8 5 12 12 15 +``` -- `hello` → `8` `5` `12` `12` `15` -- `7` `15` `15` `4` → `good` +Y *good* se convierte en esta secuencia. -Estas tablas que relacionan letras y números se llaman tablas de codificación. Además de las letras del alfabeto, las tablas de codificación incluyen signos de puntuación y otros caracteres útiles. Seguramente has encontrado tablas de codificación como [ASCII](https://es.wikipedia.org/wiki/ASCII) o [UTF-8](https://es.wikipedia.org/wiki/UTF-8). +```text +g o o d +↓ ↓ ↓ ↓ +7 15 15 4 +``` -Las distintas codificaciones contienen diferentes conjuntos de caracteres. Inicialmente, tablas pequeñas como ASCII eran suficientes para la realización de la mayoría de las tareas. Sin embargo, sólo incluían letras latinas, algunos caracteres simples como `%` y `?`, y caracteres de control especiales como el salto de línea. +El programa no sabe que eso es una palabra. Simplemente ve la instrucción «hay que mostrar el carácter con el código 8, después el del código 5, etc.». -Con la proliferación de las computadoras, diferentes países necesitaron elaborar propias tablas más amplias. Esto incluía letras cirílicas, caracteres chinos, árabe, matemáticos y tipográficos adicionales, y más tarde incluso emojis. +## ASCII. La primera codificación masiva -Hoy en día, en la mayoría de los casos se utiliza una de las variantes de [Unicode](https://es.wikipedia.org/wiki/Unicode), que incluye caracteres de casi todos los idiomas escritos del mundo. +Los primeros ordenadores trabajaban principalmente con el inglés. Para él, en los años sesenta se ideó la tabla ASCII, que incluía 128 caracteres, entre ellos el alfabeto latino, las cifras, los signos de puntuación, los caracteres especiales (@, #, !, \n) y los códigos de control. + +Eso bastaba para los primeros programas, pero no para todo el mundo. + +Cuando los ordenadores empezaron a usarse en otros países, surgió un problema. En ASCII no hay caracteres cirílicos, ideogramas, escritura árabe, acentos, símbolos de moneda, etc. + +Cada país o empresa empezó a hacer su propia codificación a partir de ASCII. + +- Windows ideó Windows-1251 para el ruso +- Apple creó Mac Roman +- Los países de Europa del Este, Asia y Oriente Medio desarrollaron sus propias variantes + +Todas esas codificaciones eran incompatibles entre sí. El código 226 en una codificación podía ser la letra é, en otra la letra и, y en una tercera un carácter técnico. Eso llevaba a un auténtico caos. + +## Cómo se veían los problemas de codificación + +Si ves en un texto esto. + +```text +¡Hola, señor! +``` + +o + +```text +?Hola, se?or! +``` + +Eso significa que el programa determinó mal la codificación del texto. Recibió una secuencia de bytes, pero los leyó con la tabla equivocada. + +Eso era la norma en los años noventa y dos mil. Un programa escribía el texto en Windows-1252, otro lo leía como ISO-8859-1, y el resultado era basura. + +## Unicode y UTF-8. El final del desorden + +Para arreglarlo todo, en los años noventa se empezó a crear la tabla universal Unicode, que contiene los caracteres de todos los sistemas de escritura del mundo, entre ellos el alfabeto latino y el cirílico, la escritura china y árabe, los signos matemáticos, la escritura del antiguo Egipto e incluso los emojis. + +Dentro de Unicode hay varios formatos de almacenamiento. El más extendido de ellos es UTF-8. Codifica de forma compacta los caracteres ingleses, pero puede ampliarse para cualquier otro. + +Hoy UTF-8 es el estándar por defecto en internet, en Python, en Linux, en las bases de datos y en los editores de código. + +## ¿Para qué necesita saber esto un programador? + +- Trabajarás con texto, y los errores de codificación siguen ocurriendo, especialmente al leer archivos, procesar datos e interactuar con API y bases de datos. +- Python usa UTF-8 por defecto, pero a veces hay que indicar la codificación de forma explícita al leer archivos, por ejemplo `open('file.txt', encoding='utf-8')`. +- Hay que saber diagnosticar los problemas. Por ejemplo, si ves «caracteres raros», eso es casi con seguridad un error de codificación. diff --git a/modules/30-variables/10-definition/es/README.md b/modules/30-variables/10-definition/es/README.md index 3a4908e2..0ab4bab2 100644 --- a/modules/30-variables/10-definition/es/README.md +++ b/modules/30-variables/10-definition/es/README.md @@ -1,33 +1,104 @@ +Imagina que hay que imprimir la frase Father! dos veces: -Imagina que necesitamos imprimir en pantalla la frase *¡Father!* dos veces. Podemos resolver esta tarea de la siguiente manera: - - ```python +```python print('Father!') print('Father!') ``` -En el caso más simple, esto funciona bien. Pero si la frase *Father!* se utiliza más de dos veces, y además en diferentes partes del programa, tendríamos que repetirla en todos los lugares, lo cual es incómodo. Los problemas con este enfoque comienzan cuando necesitamos cambiar la frase, lo cual ocurre con bastante frecuencia. Tendríamos que encontrar todos los lugares en donde se usa esta frase y realizar el reemplazo necesario. +Ese modo sirve perfectamente si la frase aparece solo un par de veces. Pero ¿qué pasa si se va a usar a menudo, en distintas partes del programa? Entonces habrá que copiar la misma expresión una y otra vez. + +¿Y qué ocurrirá si hay que cambiar la frase, por ejemplo reemplazar _Father!_ por _Mother!_? Habrá que buscar y corregir todas las apariciones a mano. Eso es incómodo y lleva a errores. -Pero hay otra forma de hacerlo. Para evitar copiar la expresión, simplemente creamos una variable con ella: +## Variables + +Para no duplicar la misma cadena, se la puede guardar en una variable e imprimir su contenido: ```python -# greeting - se traduce como saludo - greeting = 'Father!' +greeting = 'Father!' + print(greeting) print(greeting) -# => Father! -# => Father! ``` -En la línea `greeting = 'Father!'` tomamos una variable con el nombre `greeting` y le asignamos el valor `'Father!'`. La variable apunta a los datos que hemos almacenado en ella. Gracias a esto, los datos se pueden utilizar múltiples veces sin duplicarlos constantemente. +El resultado: + +```text +Father! +Father! +``` + +![Definición de una variable](./assets/variable-definition.png) + +Una **variable** es un nombre detrás del cual se guarda un valor. En nuestro ejemplo creamos una variable con el nombre `greeting` y escribimos en ella la cadena `'Father!'`. + +```text +greeting = 'Father!' + +Variable Valor +┌──────────┐ ┌──────────┐ +│ greeting │ ──→ │ 'Father!'│ +└──────────┘ └──────────┘ +``` + +La línea `greeting = 'Father!'` se lee así: «toma el valor `'Father!'` y asígnalo a la variable con el nombre `greeting`». El signo `=` aquí es el operador de asignación, no una indicación de igualdad como en matemáticas. Pone el valor dentro de la variable. + +Cuando escribimos `print(greeting)`, el intérprete sustituye el nombre `greeting` por el valor que está guardado en ella. Como resultado, en la pantalla se muestra la cadena `'Father!'`. + +```text +print(greeting) + | + v +print('Father!') +``` + +## Nombres de las variables + +Los nombres de las variables los inventa el propio programador. En Python se pueden usar: + +- Letras latinas (a-z, A-Z), +- cifras (pero no al principio), +- el guion bajo _. -Una vez que se ha creado la variable, se puede utilizar. Se inserta en los lugares donde solía estar nuestra frase. Cuando se ejecuta el código, el intérprete llega a la línea `print(greeting)` y reemplaza el contenido de la variable, y luego ejecuta el código. +Ejemplos de nombres admitidos: `greeting`, `name1`, `hello_world`. Python distingue entre minúsculas y mayúsculas. Las variables `greeting`, `Greeting` y `GREETING` son tres variables diferentes. -Para el nombre de la variable se puede utilizar cualquier conjunto de caracteres válidos, que incluyen letras del alfabeto inglés, números y el guión bajo `_`. Sin embargo, no se puede colocar un número al principio. Los nombres de las variables son sensibles a mayúsculas y minúsculas, es decir, `hola` y `HOLA` son dos nombres diferentes para dos variables diferentes. En Python, la capitulación de letras es importante, nunca lo olvides. +## Variables y literales -El número de variables que se pueden crear es ilimitado. Los programas grandes contienen decenas y cientos de miles de nombres de variables. Así es como se ven dos variables dentro de un mismo programa: +En el código es importante distinguir dónde usamos una variable y dónde escribimos un valor directamente. Eso se nota especialmente en el ejemplo con `print()`: - ```python +```python +greeting = 'Mother!' +print(greeting) # => Mother! +print('greeting') # => greeting +``` + +En el primer caso se usa la **variable** `greeting`, y el programa sustituye su valor. En el segundo caso `'greeting'` está entre comillas, por eso es un **literal de cadena**, es decir, un valor listo escrito directamente en el código. A pesar de que vemos la palabra `greeting` en los dos casos, desde el punto de vista del intérprete son cosas absolutamente distintas. + +Los literales son datos escritos de forma explícita (por ejemplo, `'Hello'`, `42`, `3.14`). Los identificadores son nombres de variables y funciones (por ejemplo, `greeting`, `print`), que apuntan a valores o comandos ya existentes. + +## El orden de uso + +Una variable hay que crearla primero (asignarle un valor) y solo después usarla. Si se intenta acceder a una variable antes de crearla, el programa dará un error: + +```python +print(name) # Error: la variable todavía no está definida +# NameError: name 'name' is not defined +name = 'Alice' +``` + +Ese error se llama «acceso a una variable no declarada». Es bastante fácil de corregir, porque el texto del error dice sin ambigüedad qué variable se usa antes de declararla. + +Y en el orden correcto todo funciona: + +```python +name = 'Alice' +print(name) # => Alice +``` + +## Varias variables en un programa + +En un mismo programa se pueden crear tantas variables como se quiera. Cada una guarda sus datos y no molesta a las demás: + +```python greeting1 = 'Father!' print(greeting1) print(greeting1) @@ -37,4 +108,8 @@ print(greeting2) print(greeting2) ``` -Para que el programa sea fácil de leer, los programadores suelen crear variables lo más cerca posible del lugar donde se utilizan. Ahora necesitamos entender cómo modificarlas. +¿Cómo saber cuándo hacen falta varias variables? La cantidad de variables depende de la lógica del programa. Ese tema se desarrolla en detalle más adelante, cuando nos encontremos con las funciones y las construcciones condicionales. + +## Dónde crear las variables + +Los programadores procuran crear las variables cerca del lugar donde se usan. Eso hace el código más legible. Es especialmente importante en programas grandes, donde las variables pueden ser decenas y cientos de miles. diff --git a/modules/30-variables/10-definition/es/assets/variable-definition.png b/modules/30-variables/10-definition/es/assets/variable-definition.png new file mode 100644 index 00000000..7ea3324c Binary files /dev/null and b/modules/30-variables/10-definition/es/assets/variable-definition.png differ diff --git a/modules/30-variables/12-change/es/README.md b/modules/30-variables/12-change/es/README.md index fc182ae8..116573d2 100644 --- a/modules/30-variables/12-change/es/README.md +++ b/modules/30-variables/12-change/es/README.md @@ -1,10 +1,9 @@ +La propia palabra «variable» sugiere que su valor puede cambiar. Esa es una de las razones principales de que las variables existan. -La palabra "variable" en sí misma sugiere que se puede cambiar. Y de hecho, dentro de un programa, los valores de las variables pueden cambiar con el tiempo. - -Por ejemplo: +Aquí tienes un ejemplo simple: ```python -# greeting - se traduce como saludo +# greeting se traduce como saludo greeting = 'Father!' print(greeting) # => Father! @@ -12,6 +11,36 @@ greeting = 'Mother!' print(greeting) # => Mother! ``` -El nombre se mantiene igual, pero los datos internos han cambiado. Es importante destacar que en Python no es necesario declarar explícitamente las variables. En su lugar, una variable se declara cuando se utiliza por primera vez en el programa. +Aquí primero escribimos en la variable una cadena (_Father!_), después otra (_Mother!_). El nombre de la variable no cambió, pero el valor de dentro pasó a ser otro. + +```text +Antes: greeting ──→ 'Father!' + ╳ +Después: greeting ──→ 'Mother!' +``` + +## ¿Para qué cambiar el valor? + +En los programas reales las variables cambian constantemente. Estas son algunas razones: + +- El programa reacciona a las acciones del usuario. Por ejemplo, mientras introduces datos en los formularios de un sitio, es muy probable que en ese momento estén cambiando constantemente las variables que contienen esos datos +- Resultados intermedios. A menudo los datos pasan por una serie de transformaciones, y en cada etapa la variable se actualiza con un valor nuevo. Un mecanismo parecido existe incluso en las calculadoras, cuando los valores intermedios se guardan con las teclas `m+` o `m-`. +- Almacenamiento del estado. Si escribes un juego, la posición del personaje, su salud, la puntuación y el nivel actual son variables que cambian constantemente. + +## Las variables se crean a medida que se usan + +En Python una variable no hay que «declararla de antemano»: aparece en el momento en que escribes por primera vez un valor en ella: + +```python +name = 'Arya' # la variable se crea aquí +``` + +Si después escribes de nuevo name = ..., eso sobrescribirá el valor anterior. Así funciona la mayoría de los lenguajes de programación modernos. + +## Por qué esto es importante + +Las variables son una forma flexible de guardar datos que pueden cambiar durante la ejecución del programa. Gracias a eso se pueden escribir programas que se comportan de distinta manera según las condiciones, las acciones del usuario o los resultados de los cálculos. + +Pero la flexibilidad tiene su otra cara. A veces cuesta entender de inmediato qué está escrito exactamente en una variable en un momento u otro. El desarrollador tiene que seguir dónde y cómo cambió, sobre todo si el código es largo. -Las variables son poderosas pero también pueden ser arriesgadas. No se puede afirmar con certeza qué hay dentro de ellas de inmediato; primero es necesario analizar el código que se encuentra antes de la variable. Esto es lo que los desarrolladores hacen durante el proceso de depuración, cuando intentan entender por qué el programa no funciona como se esperaba. +Eso es precisamente lo que se hace durante la depuración: intentar entender por qué el programa funciona de otra manera de la prevista. Se comprueban los valores de las variables, se sigue el orden de ejecución del código, se busca dónde algo salió mal. diff --git a/modules/30-variables/13-variables-naming/es/EXERCISE.md b/modules/30-variables/13-variables-naming/es/EXERCISE.md index 6928c461..957f7959 100644 --- a/modules/30-variables/13-variables-naming/es/EXERCISE.md +++ b/modules/30-variables/13-variables-naming/es/EXERCISE.md @@ -1,2 +1,5 @@ +Crea una variable que describa la cantidad máxima de intentos de entrada en el sistema y asígnale el valor `2`. Muestra el valor en pantalla. -Crea una variable que describa la cantidad de hermanos que tienes y asígnale el valor *2*. Imprime el contenido de la variable. Luego, compara tu nombre con el nombre que se utiliza en la solución del profesor. +## Extra + +Cuando el ejercicio esté superado, compara el nombre de tu variable con el nombre que se usa en la solución del profesor. diff --git a/modules/30-variables/13-variables-naming/es/README.md b/modules/30-variables/13-variables-naming/es/README.md index b3a17a16..61b6689d 100644 --- a/modules/30-variables/13-variables-naming/es/README.md +++ b/modules/30-variables/13-variables-naming/es/README.md @@ -1,23 +1,41 @@ - -Imagina que hemos creado el siguiente programa: +Imagina que tenemos este programa: ```python x = 'Father!' print(x) ``` -Ya hemos trabajado previamente con un programa similar, pero ahora hemos cambiado el nombre de la variable a `x`. +Desde el punto de vista técnico todo funciona. Ya hemos visto ejemplos parecidos, pero aquí se usa una variable con el nombre `x`. Los nombres malos dificultan leer y entender el código. Aquí tienes algunos ejemplos de variables mal elegidas: + +```python +a = "John" +n = 42 +ddr = "New York" +``` + +¿Qué variables son estas? ¿Qué guardan? Para entenderlo hay que leer todo el resto del código y adivinar por el contexto. -Al ordenador no le importa cómo llamemos a una variable u otra. Los nombres solamente son importantes para las personas. Por lo general, los programadores leen el código de otros con mucha más frecuencia que las veces que escriben el suyo propio. Para que tus colegas puedan leer y analizar tu código fácilmente, es importante dar nombres claros a las variables. +![Nomenclatura](./assets/naming.png) + +Al ordenador le da igual cómo se llame una variable. Para él, `x`, `abc`, `message` o `elephant_in_the_room` son simplemente etiquetas para guardar datos. A las personas les importa otra cosa. Los programadores leen código bastante más a menudo de lo que lo escriben. Por eso los nombres de las variables son una parte importante de la comunicación a través del código. + +## Buenos ejemplos + +```python +user_name = "Arya Stark" +unpaid_orders_count = 3 +max_attempts = 5 +``` -Es importante pensar en un nombre comprensible que refleje el significado de la variable. Al mismo tiempo, es importante dar nombres que se puedan entender sin contexto; sin tener que estudiar el código circundante. +Un buen nombre de variable ayuda a entender qué hace el programa sin leer con detalle cada línea. +Es especialmente importante dar nombres cuyo sentido se entienda sin contexto, sin necesidad de leer todo el código de alrededor. -Existe una regla generalmente aceptada: no uses transliteraciones para los nombres; solamente usa el idioma inglés. Si tienes dificultades con el inglés, utiliza un traductor. Con el tiempo, al investigar el código de otros, aprenderás a nombrar correctamente las variables. +Aquí tienes algunos consejos: -Entre los desarrolladores hay un chiste: "Los nombres de las variables son una de las cosas más difíciles en la programación". Es cierto que es difícil inventarse nombres. Por ejemplo, es difícil nombrar una variable que almacena "la cantidad de pedidos impagados de clientes con deudas del trimestre anterior". +- Usa el inglés. Es el estándar internacional. Es mejor escribir `orders_count` que `cantidad_pedidos`. Si con el inglés todavía cuesta, usa un traductor: es normal. Con el tiempo se volverá más fácil. +- Procura que el nombre refleje el sentido de la variable. Que sea un poco más largo, pero comprensible. +- No temas gastar tiempo en elegir un buen nombre. Es una inversión en la legibilidad y el mantenimiento del código. -Para poner a prueba tus habilidades, intenta hacer esta tarea: +Entre los programadores hay incluso una broma: «Algunas de las tareas más difíciles en programación son la caché y inventar nombres para las variables». A veces inventar un nombre adecuado cuesta de verdad. Aquí tienes un ejemplo: ¿cómo llamarías a una variable que guarda la cantidad de pedidos sin pagar de clientes con deuda del trimestre anterior? -> Piensa en un nombre para una variable que almacene "la cantidad de hermanos y hermanas del rey". -> -> Escribe el nombre en un bloc de notas o envíatelo por correo electrónico. No incluyas nada más que el nombre de la variable. Volveremos a este tema en unas lecciones. +Y ahora un pequeño ejercicio: inventa un nombre para una variable que vaya a guardar «la cantidad de hermanos y hermanas del rey». Escríbelo en una nota o mándatelo por correo. Solo el nombre, sin explicaciones. Volveremos a esta tarea dentro de unas lecciones. diff --git a/modules/30-variables/13-variables-naming/es/assets/naming.png b/modules/30-variables/13-variables-naming/es/assets/naming.png new file mode 100644 index 00000000..a1380ac9 Binary files /dev/null and b/modules/30-variables/13-variables-naming/es/assets/naming.png differ diff --git a/modules/30-variables/15-variables-expressions/es/README.md b/modules/30-variables/15-variables-expressions/es/README.md index f665c729..b436ac5a 100644 --- a/modules/30-variables/15-variables-expressions/es/README.md +++ b/modules/30-variables/15-variables-expressions/es/README.md @@ -1,65 +1,76 @@ +Ya sabemos que las expresiones se pueden componer de varias operaciones. Pero si se escribe todo el cálculo en una línea larga, el código se vuelve rápidamente difícil de leer. -Ya hemos aprendido cómo trabajar con variables para almacenar y reutilizar información. Pero también nos ayudan a simplificar cálculos complejos. Por ejemplo, convertir monedas o formar nuevas palabras. Veamos cómo hacerlo en la práctica. - -Supongamos que necesitamos convertir euros a yuanes a través de dólares. Estas conversiones a través de una moneda intermedia son comunes en los bancos al realizar compras en el extranjero. - -Primero, convirtamos 50 euros a dólares. Supongamos que un euro equivale a 1.25 dólares: +Por ejemplo, esta forma de escribirlo funciona: ```python -dollars_count = 50 * 1.25 -print(dollars_count) # => 62.5 +yuans_count = 50 * 1.25 * 6.91 +print(yuans_count) # => 431.875 ``` -Aquí, en la variable `cantidad_dolares = 50 * 1.25`, a la derecha del signo igual, escribimos una **expresión**. El intérprete calculará el resultado (`62.5`) y lo asignará a la variable. Al intérprete no le importa cómo se escriben los datos: `62.5` o `50 * 1.25`. Ambas son expresiones que deben calcularse. Realiza los cálculos y llega al mismo valor: `62.5`. +Python calculará esa expresión sin problema. Pero para una persona, leer ese código ya no resulta tan cómodo. Surgen preguntas de inmediato: -Cualquier cadena de texto es una expresión. La concatenación de cadenas (unir los valores de las variables) también es una expresión. Cuando el intérprete encuentra una expresión, la procesa y genera un resultado: el **valor de la expresión**. +- ¿Qué significa `1.25`? +- ¿Qué significa `6.91`? +- ¿Dónde termina un paso del cálculo y empieza el siguiente? -Aquí tienes algunos ejemplos de expresiones. A la derecha de cada expresión se muestran los valores resultantes: +Para hacer esos cálculos más claros, las variables se pueden usar dentro de otras expresiones. Primero el programa guarda el resultado intermedio en una variable, y después sustituye el valor de esa variable en el cálculo siguiente. -```python -62.5 # 62.5 -50 * 1.25 # 62.5 -120 / 10 * 2 # 24.0 -int('100') # 100 +Las variables ayudan a dividir los cálculos complejos en partes comprensibles y a guardar los resultados intermedios. -'hello' # hello -'Good' + 'will' # Goodwill -``` +## Conversión de divisas a través de una divisa intermedia -En los lugares donde se espera una expresión, puedes colocar cualquier cálculo. No solo puede ser matemático, también puede ser una cadena de texto, como la concatenación. El programa seguirá funcionando correctamente. +Imaginemos que hay que convertir euros a yuanes, pero que ese tipo de cambio directo no está disponible. Entonces lo haremos en dos pasos: **euros -> dólares -> yuanes**. Así funcionan a menudo los bancos al pagar compras en el extranjero. -Los programas están compuestos por muchas combinaciones de expresiones. Basándonos en lo que se ha dicho anteriormente, piensa si este código funcionará: +## Paso 1. Euros -> Dólares + +Supongamos el tipo de cambio: 1 euro = 1.25 dólares. Queremos convertir 50 euros: ```python -who = "dragon's " + 'mother' -print(who) +dollars_per_euro = 1.25 +dollars_count = 50 * dollars_per_euro +print(dollars_count) # => 62.5 ``` -Con variables, puedes realizar cálculos más complejos. Volvamos a nuestro programa de conversión de moneda. Guardemos el valor del dólar en yuanes como una variable separada. Calculemos el precio de 50 euros en dólares multiplicándolos por `1.25`. Supongamos que 1 dólar equivale a 6.91 yuanes: +En esa línea, `50 * dollars_per_euro` es una expresión, y `dollars_count` es la variable en la que se escribe el resultado. Python primero calcula la expresión y solo después guarda el resultado en la variable. + +Al intérprete le da igual cómo esté escrita la expresión: ```python -yuans_per_dollar = 6.91 -dollars_count = 50 * 1.25 # 62.5 -yuans_count = dollars_count * yuans_per_dollar # 431.875 +dollars_count = 62.5 +``` -print(yuans_count) +o + +```python +dollars_count = 50 * dollars_per_euro ``` -Ahora agreguemos texto a la salida utilizando la concatenación: +El resultado será el mismo. Pero para una persona la segunda variante es más útil: por el nombre `dollars_count` se ve de inmediato que en ese paso obtuvimos la cantidad en dólares. + +## Paso 2. Dólares -> Yuanes + +Ahora convertiremos 50 euros a yuanes usando el dólar como divisa intermedia. Supongamos los tipos de cambio: 1 dólar = 6.91 yuanes, 1 euro = 1.25 dólares. ```python +dollars_per_euro = 1.25 yuans_per_dollar = 6.91 -dollars_count = 50 * 1.25 # 62.5 -yuans_count = dollars_count * yuans_per_dollar # 431.875 -# La función str() convierte un número en una cadena de texto. -# Habrá una lección separada sobre estas conversiones. -print('The price is ' + str(yuans_count) + ' yuans') -# => The price is 431.875 yuans +dollars_count = 50 * dollars_per_euro +yuans_count = dollars_count * yuans_per_dollar + +print(yuans_count) ``` -Cualquier variable puede formar parte de cualquier expresión. En el momento de la evaluación, el valor de la variable se sustituye en lugar de su nombre. +Este código es más largo que la única línea `50 * 1.25 * 6.91`, pero leerlo es más fácil: + +- se ve que `1.25` es el tipo de cambio del euro al dólar +- se ve que `6.91` es el tipo de cambio del dólar al yuan +- se ve que `dollars_count` es un resultado intermedio + +Esto se notará especialmente si no vuelves al código durante al menos una semana. Y ahora imagina que en el proyecto hay cientos de miles de líneas de código. Si en esos proyectos no hubiera variables y cálculos intermedios, sería imposible entenderlos. -El intérprete calcula el valor de `dollars_count` antes de que se utilice en otras expresiones. Cuando llega el momento de usar la variable, Python ya conoce su valor porque lo ha calculado. +## Qué hay que recordar -Con variables, puedes realizar cálculos complejos y también generar una salida detallada con el valor resultante. Pero también puedes obtener nuevas expresiones al combinar dos o más valores de variables. Esto se logra mediante la concatenación. +- Si una expresión resulta demasiado larga, es mejor dividirla en varios pasos. +- Las variables ayudan a guardar los resultados intermedios y hacen los cálculos más claros. +- Cuando una variable se usa en una expresión, Python sustituye su valor y continúa el cálculo. diff --git a/modules/30-variables/18-variable-concatenation/es/README.md b/modules/30-variables/18-variable-concatenation/es/README.md index b2486f41..8cc22dfb 100644 --- a/modules/30-variables/18-variable-concatenation/es/README.md +++ b/modules/30-variables/18-variable-concatenation/es/README.md @@ -1,28 +1,76 @@ +Antes ya pegábamos cadenas directamente usando la concatenación. Ahora haremos lo mismo, pero usando variables. La buena noticia: la sintaxis sigue siendo la misma. Simplemente se sustituyen los valores de las variables. -Intentemos usar variables con concatenación, sin cambiar la sintaxis. Sabemos cómo concatenar dos cadenas: +## Pegamos dos cadenas directamente ```python -what = "Kings" + "road" +what = 'Kings' + 'road' print(what) # => Kingsroad ``` -Esto significa que podemos unir una cadena y una variable que contiene una cadena: +Aquí todo es simple: dos cadenas se unen en una. Así funciona la concatenación: el operador + suma las cadenas creando una cadena nueva. -```python -first = "Kings" -what = first + "road" +## Pegamos una cadena y una variable + +Si en la variable first está la cadena "Kings", podemos pegarla tranquilamente con otra cadena: +```python +first = 'Kings' +what = first + 'road' print(what) # => Kingsroad ``` -También podemos concatenar dos variables que contienen cadenas: +Python sustituirá el valor de la variable, realizará la operación y creará la cadena final. + +## Pegamos dos variables + +Exactamente igual se pueden unir los valores de dos variables, si las dos contienen cadenas: ```python -first = "Kings" +first = 'Kings' last = 'road' - what = first + last print(what) # => Kingsroad ``` -Las variables son una herramienta importante en la programación. Simplifican cálculos complejos y, por lo tanto, facilitan el desarrollo. Pero para trabajar con variables de manera efectiva, no solo es necesario usarlas correctamente, sino también nombrarlas correctamente. Hablaremos de esto en la próxima lección. +También se pueden añadir espacios: + +```python +full = first + " " + last +print(full) # => Kings road +``` + +```text +what = 'Kings' +who = 'road' + +what + ' ' + who +└─┬──┘ └──┬─┘ +'Kings' + ' ' + 'road' +└────────┬─────────┘ + 'Kings road' +``` + +## ¿Y qué pasa si la variable contiene un número? + +Probemos así: + +```python +age = 42 +# print("Age: " + age) # ¡Error! +``` + +El programa dará un error: no se puede sumar una cadena y un número. Para pegar una cadena con un número hay que convertir el número en cadena: + +```python +age = 42 +print('Age: ' + str(age)) # Age: 42 +``` + +Lo mismo ocurre con las variables que contienen resultados de cálculos: + +```python +price = 50 * 1.25 * 6.91 # => 431.875 +print('Price in yuans: ' + str(price)) # Price in yuans: 431.875 +``` + +La función `str()` convierte cualquier valor (un número, el resultado de un cálculo, un booleano, etc.) en una cadena. De las funciones hablaremos en detalle en lecciones futuras. diff --git a/modules/30-variables/19-naming-style/es/README.md b/modules/30-variables/19-naming-style/es/README.md index 6b2c7529..0b8f2572 100644 --- a/modules/30-variables/19-naming-style/es/README.md +++ b/modules/30-variables/19-naming-style/es/README.md @@ -1,14 +1,39 @@ +`greeting` sirve de ejemplo de un nombre de variable simple y claro. Pero a menudo nombres como `name`, `email` o `price` resultan insuficientes. Por ejemplo, hay que describir el nombre del usuario, la cantidad total de pedidos, la longitud máxima de un mensaje. Esos nombres ya constan de varias palabras. ¿Cómo se verá en ese caso el nombre de la variable? -`greeting` - es un ejemplo de nombre simple, pero no todos los nombres son tan simples como ese. A menudo, incluyen varias palabras, como "nombre de usuario". En los distintos lenguajes de programación se utilizan diferentes convenciones de codificación, por lo que el nombre de la variable será diferente. +En los distintos lenguajes de programación se usan estilos de nomenclatura distintos. De eso depende cómo se verá un nombre de variable de varias palabras. Por ejemplo, así se puede escribir una variable que guarda la longitud máxima de un mensaje: -En la nomenclatura de variables, se pueden identificar tres enfoques principales que a veces se combinan entre sí. Todos estos enfoques se aplican cuando el nombre de la variable consta de varias palabras: +1. `maxmessagelength` +1. `maxMessageLength` +1. `max-message-length` +1. `max_message_length` -* **kebab-case** - las partes de la variable se separan con guiones (`my-super-var`) -* **snake\_case** - se utiliza un guión bajo para separar (`my_super_var`) -* **CamelCase** - cada palabra en la variable se escribe con mayúscula (`MySuperVar`) +## Los estilos principales -En Python, las variables se nombran en estilo `snake_case`: las palabras se escriben en minúsculas y se separan con el carácter de guión bajo `_`. Para obtener más información, puedes consultar la sección ["Cómo nombrar variables"](https://peps.python.org/pep-0008/#naming-conventions) en el estándar PEP8. +Estos son tres enfoques populares para escribir nombres compuestos: -En [Hexlet](https://ru.hexlet.io/?utm_source=code-basics&utm_medium=referral&utm_campaign=programs&utm_content=lesson), comenzamos a trabajar con linters casi desde el principio. +- kebab-case: las palabras se separan con guion: `max-message-length`. -A continuación, veremos ejemplos de malas prácticas y analizaremos por qué se deben evitar. + En Python no funciona, ya que el guion (-) se percibe como el operador de resta. + +- snake_case: las palabras se separan con subrayado: `max_message_length`. + + Es el estándar para Python. + +- CamelCase (o UpperCamelCase): cada palabra con letra mayúscula, sin separadores: `maxMessageLength`. + +## Cómo se hace correctamente en Python + +```python +user_name = "Daenerys" +max_length = 280 +total_orders_count = 17 +``` + +- Todas las letras en minúscula +- Las palabras se separan con el carácter de subrayado + +## Cómo no hay que hacerlo + +No conviene incluir el tipo de dato en el nombre de la variable. Esos nombres se leen peor y quedan obsoletos rápido. Por ejemplo, `user_name_string` o `messages_number` describen no el sentido de la variable, sino su implementación técnica. + +El nombre debe responder a la pregunta «¿qué se guarda?», no «¿de qué tipo es?». Por eso es mejor escribir `user_name` en lugar de `user_name_string`, y `messages_count` en lugar de `messages_number`. diff --git a/modules/30-variables/20-magic-numbers/es/README.md b/modules/30-variables/20-magic-numbers/es/README.md index 8d16d187..3f987582 100644 --- a/modules/30-variables/20-magic-numbers/es/README.md +++ b/modules/30-variables/20-magic-numbers/es/README.md @@ -1,5 +1,4 @@ - -Tomemos como ejemplo un programa que calcula el tipo de cambio de divisas: +Veamos un ejemplo de programa que realiza una conversión de divisas: ```python euros_count = 1000 @@ -9,33 +8,31 @@ rubles_count = dollars_count * 60 # 75000.0 print(rubles_count) ``` -Desde el punto de vista del desarrollo profesional, este código no cumple con las "mejores prácticas". +Técnicamente el código funciona. Pero desde el punto de vista del desarrollo profesional, ese código se considera una mala práctica. + +## ¿Cuál es el problema? -En este ejemplo, es difícil entender qué significan los números `60` y `1.25`. Imagina que tienes que trabajar con este código después de un mes o un año, será complicado. También será difícil para otros programadores que no hayan visto el código antes. +En las expresiones se han usado números incomprensibles: __1.25__ y __60__. ¿Qué valores son esos? ¿El tipo de cambio? ¿De dónde salieron? Dentro de un mes o de un año, lo más probable es que no recuerdes qué significan exactamente esos números. Y si el código lo abre otro desarrollador, simplemente no entenderá de dónde sale todo. Esos números se llaman mágicos. -En nuestro ejemplo, es fácil deducir el contexto porque las variables están nombradas correctamente. Pero en proyectos reales, el código es mucho más complejo, por lo que a menudo es imposible adivinar el significado de los números. +Los números mágicos (magic numbers) son valores numéricos cuyo sentido no queda claro a partir del código. Normalmente se usan directamente en expresiones matemáticas, sin variables con nombres comprensibles. Para entender su propósito hay que meterse en el contexto o leer documentación adicional. Los números mágicos dificultan la lectura, la comprensión y el mantenimiento del código. -El problema radica en los "números mágicos" — magic numbers. Estos son números cuyo origen no se puede entender a simple vista, es necesario profundizar en lo que sucede en el código. +## Cómo evitar la magia -Para evitar este problema, es necesario crear variables con nombres adecuados. Así todo encajará en su lugar: +La forma más simple consiste en llevar esos valores a variables con nombres comprensibles. Entonces el sentido se volverá evidente: - ```python +```python dollars_per_euro = 1.25 rubles_per_dollar = 60 euros_count = 1000 -dollars_count = euros_count * dollars_per_euro # 1250.0 -rubles_count = dollars_count * rubles_per_dollar # 75000.0 +dollars_count = euros_count * dollars_per_euro # 1250.0 +rubles_count = dollars_count * rubles_per_dollar # 75000.0 print(rubles_count) ``` -En este programa: - -* Se utiliza la convención de nombres snake_case -* Las dos nuevas variables se separan de los cálculos posteriores con una línea en blanco. Estas variables tienen sentido incluso sin los cálculos, por lo que esta separación es apropiada ya que mejora la legibilidad. -* El código resultante está bien nombrado y estructurado, pero es más largo que la versión anterior. Esto ocurre a menudo y es normal, ya que el código debe ser legible. +Los números en sí no han desaparecido, pero ahora están escritos en variables cuyos nombres dicen sin ambigüedad qué son. -Los números mágicos y los nombres de variables confusos no rompen el código, pero lo hacen menos legible. +## Conclusión -Es importante entender que la computadora ejecutará el cálculo especificado de todas formas. Sin embargo, otro programador que lea el código no entenderá nada, lo que dificultará el trabajo. Nombrar adecuadamente las variables es la mitad del éxito en el análisis del código. +Los números mágicos hacen el código incomprensible y difícil de mantener. Para evitar ese problema hay que reemplazar esos números por variables con nombres significativos. Eso hace el código más legible, especialmente a largo plazo. Un código claro es siempre más importante que la compacidad, aunque el programa se alargue un poco. diff --git a/modules/30-variables/23-constants/es/README.md b/modules/30-variables/23-constants/es/README.md index 474cfaed..c24222f0 100644 --- a/modules/30-variables/23-constants/es/README.md +++ b/modules/30-variables/23-constants/es/README.md @@ -1,9 +1,43 @@ +A veces en un programa aparecen valores que nunca deben cambiar. Por ejemplo: -Algunos datos nunca cambian, como las constantes matemáticas. Tomemos como ejemplo el número π. Siempre es igual a 3.14 y no puede cambiar. Para acceder a este tipo de datos, en Python se utilizan constantes: +- La constante matemática π (pi). +- El tipo de cambio del dólar en una fecha determinada. +- La comisión fija de un servicio. + +Esos valores se llaman constantes y se acostumbra a distinguirlos de las variables normales, para que no surja la tentación de cambiarlos. + +## Ejemplo: el número π ```python PI = 3.14 print(PI) # => 3.14 ``` -Una constante se crea de la misma manera que una variable. La única diferencia es que las constantes se suelen nombrar con letras mayúsculas y se usa `_` como separador entre palabras. Una constante, al igual que una variable, se puede utilizar en cualquier expresión. +Aquí PI es una constante que guarda el valor del número π. El sentido de una constante es que su valor no debe cambiar durante el funcionamiento del programa. + +## ¿En qué se diferencia una constante de una variable? + +El concepto de constante está extendido en la mayoría de los lenguajes de programación. Allí la constante es a menudo una entidad aparte del lenguaje, cuyo valor no se puede modificar. En Python las constantes como concepto aparte del lenguaje no existen, pero hay un acuerdo: si una variable está escrita con letras en mayúscula, es una constante. + +```python +PI = 3.14 +PI = 3.14159 # Técnicamente es posible, pero no se acostumbra a hacerlo +``` + +Pero por convención, si una variable está nombrada con letras mayúsculas, se considera una constante y no se debe cambiar. + +## Cómo se formatean las constantes + +- Todas las letras en mayúscula +- Las palabras se separan con el carácter de subrayado `_` +- El estilo se llama UPPER_SNAKE_CASE (también se lo llama SCREAMING_SNAKE_CASE) + +```python +PI = 3.14 +MAX_USERS = 100 +DEFAULT_TIMEOUT = 30 +``` + +## ¿Para qué hacen falta las constantes? + +Las constantes hacen el código más claro y más seguro. Ayudan a ver de inmediato qué valores del programa se consideran fijos y no deben cambiar. Eso es especialmente importante al trabajar con datos como las constantes matemáticas y físicas, los ajustes por defecto o los límites fijos. El uso de constantes reduce el riesgo de errores: por el nombre de la variable se entiende de inmediato que tenemos delante una constante y que no conviene cambiarla. Además, si el valor hay que cambiarlo de todos modos (por ejemplo, en los ajustes), basta con modificarlo en un solo lugar y el cambio se recogerá automáticamente en todo el programa. diff --git a/modules/31-advanced-strings/25-interpolation/es/README.md b/modules/31-advanced-strings/25-interpolation/es/README.md index 277520a0..678a910a 100644 --- a/modules/31-advanced-strings/25-interpolation/es/README.md +++ b/modules/31-advanced-strings/25-interpolation/es/README.md @@ -1,7 +1,4 @@ - -Ya sabes cómo concatenar cadenas de texto o "unir" varias cadenas para obtener una nueva expresión. Pero en programación, hay alternativas a estas operaciones. Estas mejoran la legibilidad del código y lo hacen más fácil de mantener. - -El método básico para unir cadenas es la **concatenación**. Con la concatenación, las cadenas se "suman" entre sí, como se muestra en el siguiente ejemplo: +Recordemos cómo funciona la concatenación. Para ello unimos las cadenas necesarias (o las variables con cadenas dentro) mediante el signo `+`. ```python first_name = 'Joffrey' @@ -11,7 +8,9 @@ print(greeting + ", " + first_name + "!") # => Hello, Joffrey! ``` -La concatenación funciona de manera sencilla, pero no siempre es visualmente clara. Debido a las comillas, es difícil ver cómo será el resultado final. Y cuanto más compleja sea la cadena, más confusa se volverá. La interpolación es una alternativa a la concatenación. Así es como se ve: +Pero, al mismo tiempo, en expresiones complejas cuesta ver de inmediato qué texto se obtendrá al final. Sobre todo si en la cadena hay espacios, comas o comillas: empiezan a estorbar la lectura. Incluso el ejemplo actual exige un pequeño esfuerzo para entender cómo saldrá la cadena. + +Por esa razón, en muchos lenguajes existe una operación llamada interpolación. La interpolación es una forma de insertar los valores de las variables directamente dentro de una cadena. En Python para eso se usan las f-strings (o cadenas de formato). Así: ```python first_name = 'Joffrey' @@ -21,9 +20,29 @@ print(f'{greeting}, {first_name}!') # => Hello, Joffrey! ``` -La letra `f` indica que estamos creando una **f-string** (cadena f) - una plantilla en la que los valores de las variables se insertan mediante llaves. El resultado es una cadena normal. +La letra f delante de las comillas indica que dentro de la cadena se pueden usar variables. Sus nombres se escriben entre llaves, y Python sustituye automáticamente los valores necesarios. + +```text +first_name = 'Joff' +greeting = 'Hello' -Veamos este ejemplo: +f'{greeting}, {first_name}!' + └───┬───┘ └────┬─────┘ + 'Hello' 'Joff' + └──────┬───────┘ + 'Hello, Joff!' +``` + +Compara estos dos ejemplos uno al lado del otro: + +```python +print(greeting + ", " + first_name + "!") +print(f'{greeting}, {first_name}!') +``` + +La segunda variante es más simple y más clara. + +## Ejemplo ```python school = 'Hexlet' @@ -32,6 +51,12 @@ what_is_it = f'{school} - online courses' print(what_is_it) # => Hexlet - online courses ``` -En casi todos los lenguajes de programación, la interpolación es preferible a la concatenación para unir cadenas. La cadena resultante se muestra como una sola unidad, y los espacios y otros caracteres son fácilmente distinguibles dentro de ella. +Esa forma de escribir se lee con facilidad: los espacios, los guiones y los símbolos se ven de inmediato. La cadena se parece exactamente a como aparecerá en la salida. Eso hace que el código sea claro y cómodo de mantener. Por esa razón, en la mayoría de los lenguajes la interpolación es preferible a la concatenación. + +## Por qué esto importa + +La interpolación es preferible a la concatenación en casi todos los lenguajes de programación modernos. Ella: -La interpolación ayuda a que el código sea más comprensible para los desarrolladores. Pero esta no es la única alternativa útil que queríamos mencionar. En el próximo tema, aprenderemos cómo declarar cadenas de varias líneas sin usar `\n`. +- Simplifica la estructura de las cadenas. +- Mejora la legibilidad del código. +- Reduce la cantidad de errores al trabajar con espacios y signos de puntuación. diff --git a/modules/31-advanced-strings/30-symbols/es/README.md b/modules/31-advanced-strings/30-symbols/es/README.md index 12fcb847..21ac5d91 100644 --- a/modules/31-advanced-strings/30-symbols/es/README.md +++ b/modules/31-advanced-strings/30-symbols/es/README.md @@ -1,50 +1,92 @@ +A veces hace falta obtener un solo carácter de una cadena. Por ejemplo, si el sitio conoce el nombre y el apellido del usuario y hay que mostrarlos en formato abreviado, del tipo A. Ivanov. Para eso se necesita tomar la primera letra del nombre. -A veces es necesario obtener un solo carácter de una cadena. Por ejemplo, si un sitio web conoce el nombre y apellido del usuario, y en algún momento se requiere mostrar esta información en el formato *A. Ivanov*. Para lograr esto, la computadora necesita tomar el primer carácter del nombre. En Python, hay una operación adecuada que estudiaremos hoy. +![Extracción de caracteres de una cadena](./assets/symbols.png) -Supongamos que queremos mostrar en pantalla solo la primera letra del nombre "Alexander". Se vería así: +En Python, para acceder a los caracteres de una cadena se usa la indexación. La indexación significa que cada carácter de la cadena tiene su propio número, es decir, su índice. La indexación empieza en cero: el primer carácter tiene el índice `0`, el segundo el `1` y así sucesivamente. Imaginemos que tenemos una cadena: ```python first_name = 'Alexander' +``` + +Para obtener la primera letra, indicamos su posición (el índice) entre corchetes: +```python print(first_name[0]) # => A ``` -La operación con corchetes y un número extrae el elemento por su **índice** - la posición del carácter dentro de la cadena. Los índices comienzan en 0 en casi todos los lenguajes de programación. Por lo tanto, para obtener el primer carácter, debemos especificar el índice `0`. El índice del último elemento es igual a la longitud de la cadena menos uno. Acceder a un índice fuera de los límites de la cadena resultará en un error: +Los índices en Python (y en muchos lenguajes) empiezan en cero: -```python -# La longitud de la cadena es 9, por lo que el último índice es 8 -first_name = 'Alexander' +```text +Carácter A l e x a n d e r +Índice 0 1 2 3 4 5 6 7 8 +``` -print(first_name[8]) # => r +La longitud de la cadena `Alexander` es `9`, por eso el índice del último carácter es `8`, es decir, `9 - 1`. -print(first_name[9]) -IndexError: string index out of range +Para obtener, por ejemplo, el último carácter, se puede escribir: + +```python +print(first_name[8]) # => r ``` -Para reforzar los nuevos conocimientos, eche un vistazo al siguiente código y piense en qué imprimirá: +Si cambia la longitud de la cadena, el último elemento también se desplaza y habrá que indicar el nuevo índice en el que está ese carácter. + +Y si se sale de los límites de la cadena, obtendremos un error: ```python -magic = '\nyou' -print(magic[1]) # => ? +print(first_name[9]) +# IndexError: string index out of range ``` -También hay situaciones que no son estándares o comunes. Por ejemplo, es posible que necesitemos mostrar un elemento desde el final, y además, desde una expresión con un gran número de caracteres. En este caso, podemos usar un índice negativo, que facilitará el trabajo del programador. +Por eso, en programación se acostumbra a comprobar la longitud de la cadena y acceder a sus caracteres solo cuando es seguro. Llegaremos a eso en lecciones futuras. + +## Extracción abreviada desde el final -Se pueden usar índices negativos. En este caso, se accede a los caracteres desde el final de la cadena. `-1` es el índice del último carácter, `-2` es el penúltimo, y así sucesivamente. A diferencia de la indexación directa, el recuento inverso comienza desde `-1`: +Para obtener elementos desde el final es mejor usar índices negativos. En ese caso la cuenta empieza por el final. ```python -first_name = 'Alexander' +print(first_name[-1]) # => r, el último carácter +print(first_name[-2]) # => e, el penúltimo carácter +``` -print(first_name[-1]) # => r +```text +Cadena: 'H' 'e' 'x' 'l' 'e' 't' +Índice: 0 1 2 3 4 5 +Del final:-6 -5 -4 -3 -2 -1 ``` -El índice puede ser no solo un número específico, sino también el valor de una variable. Mire el siguiente ejemplo. Aquí hemos escrito el índice dentro de los corchetes como una variable. Este código dará el mismo resultado, es decir, mostrará en pantalla el carácter *A*: +Los índices negativos funcionan así: + +- -1 corresponde al último carácter +- -2 corresponde al penúltimo +- y así sucesivamente + +Es cómodo y seguro, porque funciona correctamente incluso si la cadena cambia de longitud. + +El índice se puede guardar en una variable: ```python -first_name = 'Alexander' index = 0 - print(first_name[index]) # => A ``` -Para mostrar solamente algunos caracteres de una expresión, no es necesario escribir una gran cantidad de líneas de código, simplemente extraiga el elemento utilizando un índice. También puede usar un índice negativo para facilitar la extracción de caracteres desde el final de la expresión. A continuación, veremos cómo usar estos conocimientos para extraer subcadenas de una cadena. +Este enfoque es útil cuando el índice se calcula en algún lugar del código y después se usa para acceder al carácter necesario. + +## Caracteres especiales + +En la indexación se cuentan las letras normales, los signos y los caracteres especiales. Todos ocupan una posición en la cadena y tienen su índice, aunque «no se vean» en la pantalla. + +Por ejemplo, en la cadena `\nyou` el primer carácter es `\n` (el salto de línea), y en el índice 1 ya está la letra `y`. Por eso el acceso `magic[1]` devolverá precisamente `y`. + +## Piensa: ¿qué mostrará este código? + +```python +magic = '\nyou' +print(magic[1]) # => ? +``` + +La salida será: + +```text +y +``` diff --git a/modules/31-advanced-strings/30-symbols/es/assets/symbols.png b/modules/31-advanced-strings/30-symbols/es/assets/symbols.png new file mode 100644 index 00000000..6093469e Binary files /dev/null and b/modules/31-advanced-strings/30-symbols/es/assets/symbols.png differ diff --git a/modules/31-advanced-strings/70-slices/es/README.md b/modules/31-advanced-strings/70-slices/es/README.md index 4181661a..368df072 100644 --- a/modules/31-advanced-strings/70-slices/es/README.md +++ b/modules/31-advanced-strings/70-slices/es/README.md @@ -1,116 +1,174 @@ +Al trabajar con cadenas nos encontramos a menudo con una tarea: extraer una parte de la cadena. Por ejemplo, obtener el año de una fecha, el nombre de un nombre completo o los primeros caracteres de una dirección de correo electrónico. Para esos casos, en Python existe una herramienta potente y cómoda: los cortes (o slices). -Cuando trabajamos con cadenas en programación, a menudo necesitamos extraer una parte de ellas. Por ejemplo, necesitamos determinar si una subcadena está presente dentro de una cadena más grande. En esta lección aprenderemos cómo hacerlo. +## ¿Qué es una subcadena? -Una **subcadena** es una parte de una cadena que se debe encontrar y extraer. +Una subcadena es una parte de una cadena. Lo que está contenido dentro de otra cadena. Por ejemplo, en la cadena '12-08-2034' una subcadena puede ser '2034', '12' o incluso '-'. Todo depende de qué información necesitemos extraer. -Supongamos que tenemos una fecha en el siguiente formato: *12-08-2034*. Necesitamos extraer de ella la subcadena que contiene solo el año. +Supongamos que tenemos una cadena con la fecha '12-08-2034'. Queremos obtener de ella solo el año, '2034'. En esta cadena cada carácter tiene un índice (una posición), empezando por cero: -Si pensamos lógicamente, debemos calcular el índice del carácter donde comienza el año y luego extraer cuatro caracteres. Los índices en una cadena comienzan desde cero, lo que significa que el primer carácter del año está disponible en el índice 6, y el último carácter está disponible en el índice 9. Verifiquemos: - -```python -value = '12-08-2034' - -print(value[6]) # => 2 -print(value[9]) # => 4 +```text +'1' '2' '-' '0' '8' '-' '2' '0' '3' '4' + 0 1 2 3 4 5 6 7 8 9 ``` -Conociendo estos índices, podemos usar recortes para obtener la subcadena deseada: +Resulta que el año empieza en el índice 6 y termina en el 9. Para extraerlo usamos un corte: ```python value = '12-08-2034' - year = value[6:10] print(year) # => 2034 ``` -Los **recortes de cadenas** en Python son un mecanismo mediante el cual se extrae una subcadena según los parámetros especificados. En el ejemplo anterior, tomamos una subcadena desde el índice 6 hasta el índice 10, sin incluirlo, es decir, desde el 6 hasta el 9 inclusive. La fórmula se ve así: +El corte `value[6:10]` toma los caracteres desde el índice 6 hasta el 9 inclusive. El formato del corte: + +```python +cadena[inicio:fin] +``` + +El carácter cuyo índice se indica como «fin» no se incluye. Se puede entender como el orden natural del carácter que hay que tomar como último. Es decir, si ahí está el 11, significa que será el carácter número 11 por orden. ```python -str[inicio:fin] +# El último carácter tiene el índice 10, y en total hay 11 letras +value = 'code-basics' + +print(value[5:11]) # => basics (del índice 5 al 10) +print(value[0:7]) # => code-ba (del índice 0 al 6) +print(value[2:6]) # => de-b +print(value[:4]) # => code +print(value[5:]) # => basics +``` + +¿Cómo se cuenta todo esto? Si trabajamos con una cadena concreta, casi siempre se hace a ojo. + +## El corte es una cadena -# Algunos ejemplos +```python value = '01-12-9873' -# Un recorte de cadena siempre es una cadena, -# incluso si el contenido original era un número. -value[1:2] # '1' -value[3:5] # '12' +value[1:2] # => 1 +value[3:5] # => 12 ``` -Los recortes tienen muchas variaciones. Por ejemplo, si no se especifica el límite final, se extraerá hasta el final de la cadena. Lo mismo ocurre con el límite inicial, que es el comienzo de la cadena: +Un corte siempre devuelve una cadena, incluso si dentro solo hay dígitos. Eso significa que el resultado se puede usar como una cadena normal: concatenarlo, imprimirlo, pasarlo a funciones y así sucesivamente. ```python -value = 'Hexlet' -value[3:] # 'let' -value[:3] # 'Hex' +part = value[3:7] # => 12-9 +print(part[0:2]) # => 12 ``` -Incluso se pueden usar índices negativos. En este caso, el conteo se realiza desde el final: +Primero obtuvimos la subcadena `'12-9'`, y después hicimos de ella un nuevo corte, `'12'`. + +## Corte hasta el final o desde el principio + +A veces hay que tomar una parte de la cadena hasta el final o desde el principio. Esas situaciones también se resuelven con facilidad mediante cortes: ```python value = 'Hexlet' -# El límite derecho es negativo. Restamos 1 desde el final de la cadena -value[3:-1] # 'le' -# El límite izquierdo es negativo. Restamos 5 desde el final de la cadena -value[-5:3] # 'ex' + +print(value[3:]) # => let # Del carácter 3 al final +print(value[:3]) # => Hex # Del principio al carácter 3 ``` -Los recortes tienen dos parámetros obligatorios, pero a veces se usa un tercer parámetro. +Si no se indica el límite, Python pondrá por su cuenta el valor necesario: el principio de la cadena o su final. + +## ↩ Cortes con índices negativos -Los recortes tienen un tercer parámetro opcional llamado **paso de extracción**. Por defecto, es igual a uno, pero podemos cambiarlo: +Python permite contar de izquierda a derecha y desde el final de la cadena. Para eso se usan los índices negativos. ```python value = 'Hexlet' -value[1:5:2] # el -# 1:5 es 'exle' -# paso 2 significa cada segundo, es decir, 'e' y 'l' + +print(value[-1]) # => t # El último carácter +print(value[3:-1]) # => le # Del 3 al penúltimo +print(value[-5:3]) # => ex # Del 1 al 2 ``` -Todo esto se puede combinar con límites abiertos, es decir, sin especificar el inicio o el final: +El índice `-1` en este caso corresponde al último carácter de la cadena. Por eso aquí vemos un resultado distinto: ```python -value = 'Hexlet' -value[:5:2] # 'Hxe' -value[1::2] # 'elt' +print(value[3:-1]) # => le +print(value[3:]) # => let ``` -El paso puede ser negativo, en cuyo caso se toma desde el final. Esto lleva al uso más común del paso: **invertir una cadena**: +Es cómodo cuando no se conoce de antemano la longitud de la cadena, pero hay que tomar la «cola» o la «parte media» de la cadena respecto al final. + +## El paso en los cortes + +El corte tiene un tercer parámetro llamado paso. Por defecto el paso es igual a 1, es decir, los caracteres van seguidos. Pero el paso se puede cambiar, por ejemplo para tomar cada segundo carácter: ```python value = 'Hexlet' -# Omitimos ambos límites -value[::-1] # 'telxeH' + +print(value[1:5:2]) # => el +# Índices 1, 3 → caracteres e, l ``` -Si se utiliza un paso negativo y los elementos del recorte se extraen en orden inverso, entonces los límites del recorte también deben especificarse en orden inverso. El límite derecho se especifica primero, seguido del límite izquierdo: +En este ejemplo: + +- 1:5 define el corte 'exle' +- el paso 2 significa que tomamos un carácter de cada dos: 'e' y 'l' + +Se puede combinar con límites abiertos: + +```python +print(value[:5:2]) # => Hxe +print(value[1::2]) # => elt +``` + +## Invertir una cadena + +El paso negativo permite invertir una cadena, lo que es uno de los «trucos» más populares de los cortes. Para eso se usa un paso negativo: ```python value = 'Hexlet' -# El carácter con índice 1 no se incluirá en la subcadena -value[4:1:-1] # 'elx' + +print(value[::-1]) # => telxeH ``` -Los recortes se pueden especificar no sólo con números, sino también con variables: +La cadena se lee del final al principio. Muy cómodo y conciso. + +## Cortes con paso negativo + +Si usas un paso negativo, recuerda: los índices también hay que indicarlos en orden inverso. Si no, el corte no funcionará (devolverá una cadena vacía): ```python value = 'Hexlet' + +print(value[4:1:-1]) # => elx +# Índices: 4, 3, 2 → e, l, x +``` + +Python empezará por el índice 4 e irá hacia la izquierda hasta el índice 2 inclusive. El índice 1 no entra en el resultado. + +## Corte con variables + +Los cortes no tienen que estar fijados con números. Se pueden usar variables: + +```python start = 1 end = 5 -value[start:end] # 'exle' + +value = 'Hexlet' +print(value[start:end]) # => exle ``` -Resumiendo todo: +Esto resulta especialmente útil cuando los límites se calculan durante la ejecución del programa. + +## Chuleta rápida ```python value = 'Hexlet' -value[::] = 'Hexlet' # Toda la cadena -value[:] = 'Hexlet' # Toda la cadena -value[::2] = 'Hxe' # Caracteres en posiciones pares -value[1::2] = 'elt' # Caracteres en posiciones impares -value[::-1] = 'telxeH' # Toda la cadena en orden inverso -value[5:] = 't' # Cadena a partir del sexto carácter -value[:5] = 'Hexle' # Cadena hasta el sexto carácter -value[-2:1:-1] = 'elx' # Todos los caracteres desde el penúltimo hasta el tercero en orden inverso. En todos los casos, al seleccionar desde un índice mayor a uno menor, se debe especificar el paso + +value[::] # Hexlet — la cadena completa +value[:] # Hexlet +value[::2] # Hxe — los caracteres pares +value[1::2] # elt — los caracteres impares +value[::-1] # telxeH — la cadena en orden inverso +value[5:] # t +value[:5] # Hexle +value[-2:1:-1] # elx — del penúltimo al tercero ``` -Como puedes ver, los recortes pueden hacer muchas cosas. No te preocupes si no recuerdas todas estas combinaciones en este momento, es normal. Con el tiempo aprenderás a usarlos sin consultar la documentación. +Cuando hagas un corte de un índice mayor a uno menor, define obligatoriamente un paso negativo, porque si no el corte no funcionará. + +No te preocupes si ahora no memorizas todas las combinaciones: empezarás a usarlas en la práctica muy pronto. Lo principal es entender cómo funciona la estructura básica `cadena[inicio:fin:paso]`. diff --git a/modules/31-advanced-strings/90-multiline-strings/es/README.md b/modules/31-advanced-strings/90-multiline-strings/es/README.md index abad74f8..f2610a04 100644 --- a/modules/31-advanced-strings/90-multiline-strings/es/README.md +++ b/modules/31-advanced-strings/90-multiline-strings/es/README.md @@ -1,70 +1,114 @@ +A veces el texto de un programa tiene que estar formado por varias líneas. Por ejemplo, al generar un correo, al crear una plantilla, al formatear un mensaje de error o simplemente al trabajar con textos largos. -Imagina que necesitamos definir una cadena que consta de varias líneas, es decir, que contiene saltos de línea `\n`. Por ejemplo, se vería así: +Por supuesto, se puede usar el carácter de salto de línea `\n`, como hacíamos antes: ```python -text = 'Ejemplo de texto,\ncompuesto por\nvarias líneas' +text = 'Un ejemplo de texto,\nformado por\nvarias líneas' ``` -Al imprimir la cadena, se mostrará de la siguiente manera: +Al imprimirla, la cadena se verá así: ```text -Ejemplo de texto, -compuesto por +Un ejemplo de texto, +formado por varias líneas ``` -Para este tipo de situaciones, en Python existe otra forma de crear cadenas llamada **cadenas de varias líneas**. Para definir una cadena de varias líneas, debemos encerrarla entre triples comillas `"""` o `'''`. Dentro de una cadena de varias líneas, podemos agregar texto sin necesidad de utilizar saltos de línea `\n`: +Pero esa forma resulta incómoda, sobre todo si la cadena es larga o si hay que añadir saltos nuevos a menudo. Cada `\n` hay que insertarlo a mano, y eso empeora la legibilidad del código. + +## Las cadenas multilínea (multi-line) como alternativa + +En Python existe una forma más cómoda de escribir texto en varias líneas: las cadenas multilínea (multi-line strings). Para crear una cadena así hay que encerrar el texto entre comillas triples (técnicamente es una comilla repetida tres veces): `'''` o `"""`: + +```python +text = '''Un ejemplo de texto, +formado por +varias líneas''' +``` + +Ahora en el código todo se ve igual que en la salida: + +```text +Un ejemplo de texto, +formado por +varias líneas +``` + +## Cuidado con la línea vacía al final + +Si cierras las comillas triples en una línea nueva, Python incluye también esa línea en el resultado: ```python -text = '''Ejemplo de texto, -compuesto por +text = '''Un ejemplo de texto, +formado por varias líneas ''' + +print("====") +print(text) +print("====") ``` +La salida: + ```text -Ejemplo de texto, -compuesto por +==== +Un ejemplo de texto, +formado por varias líneas +==== ``` -Observa que al final del texto hay una línea vacía. Esto se debe a que colocamos las comillas de cierre `'''` en una nueva línea. Si no colocamos las comillas de cierre en una nueva línea, la cadena no tendrá una línea vacía: +Fíjate: aparece una línea vacía al final. Para evitarla, no pases las comillas de cierre a una línea nueva: ```python -text = '''Ejemplo de texto, -compuesto por +text = '''Un ejemplo de texto, +formado por varias líneas''' ``` +La salida: + ```text -Ejemplo de texto, -compuesto por +==== +Un ejemplo de texto, +formado por varias líneas +==== ``` -Gracias a las triples comillas, no es necesario escapar las comillas dentro de la cadena: +## Ventajas de las cadenas multi-line -```text -Aquí no es necesario escapar las comillas 'simples' o "dobles" +- Legibilidad del código: el texto en el código se ve casi como en la pantalla. +- Comodidad al editar: es fácil añadir, borrar y cambiar líneas. +- No hace falta escapar las comillas: + +```python +quote = '''Aquí no hay que escapar ni las comillas 'simples' ni las "dobles"''' ``` -Además, las cadenas de varias líneas pueden ser utilizadas como f-strings para interpolación: +## Interpolación dentro de una cadena multilínea + +Las cadenas multilínea se pueden combinar con las f-strings para sustituir valores de variables: ```python a = 'A' b = 'B' -# Se agregó una f al inicio text = f'''{a} y {b} -se sentaron en un tubo -''' +en líneas distintas''' ``` +La salida: + ```text A y B -se sentaron en un tubo - +en líneas distintas ``` -Para la computadora no importa qué método de concatenación y saltos de línea utilices. De todas formas, realizará los cálculos y mostrará el resultado correcto. La interpolación y las cadenas de varias líneas se utilizan para facilitar la lectura del código por parte de los desarrolladores. +Esto resulta especialmente cómodo para plantillas, correos, mensajes de error y descripciones multilínea. + +## El ordenador y la persona perciben el código de forma distinta + +Python puede procesar tanto las cadenas con `\n` como las cadenas multilínea. Para el intérprete son lo mismo. Pero para la persona que lee el código, las cadenas multi-line son mucho más cómodas y claras. diff --git a/modules/33-data-types/40-primitive-data-types/es/README.md b/modules/33-data-types/40-primitive-data-types/es/README.md index 9f602cf2..0ead2790 100644 --- a/modules/33-data-types/40-primitive-data-types/es/README.md +++ b/modules/33-data-types/40-primitive-data-types/es/README.md @@ -1,24 +1,89 @@ +Los programas trabajan con información distinta: texto, números, fechas, listas, valores booleanos. Cada valor de un programa tiene un tipo. -Hay diferentes formas de representar datos en los programas. También hay **cadenas de texto** - conjuntos de caracteres entre comillas como `"Hello, World!"`. Hay **números enteros** - por ejemplo, `7`, `-198`, `0`. Estas son dos categorías diferentes de información, dos **tipos de datos** diferentes. La operación de multiplicación tiene sentido para la categoría de "números enteros", pero no para la categoría de "cadenas de texto": no tiene sentido multiplicar la palabra "mamá" por la palabra "cuaderno". +Por ejemplo: -El tipo de datos determina qué se puede hacer con los elementos de un conjunto de información específico. En esta lección, aprenderemos sobre los tipos de datos y cómo funciona la tipificación en Python. +- "Hello, World!" es una cadena (`str`) +- 7, -198, 0 son números enteros (`int`) +- 3.14, -0.01, 1.0 son números racionales (`float`) -El lenguaje de programación reconoce los tipos de datos, por lo que Python no nos permitirá multiplicar una cadena de texto por otra cadena de texto; no se puede multiplicar texto por texto. Sin embargo, se puede multiplicar un número entero por otro número entero. La presencia de tipos y de tales restricciones en el lenguaje protege a los programas de errores accidentales. +## ¿Qué es un tipo de dato? -A diferencia de las cadenas de texto, los números no necesitan estar entre comillas. Para imprimir el número 5, simplemente escriba: +Un tipo de dato determina: + +- qué valores le corresponden; +- qué operaciones se pueden hacer con él. + +Por ejemplo, los números se pueden sumar, dividir y multiplicar. Las cadenas, en cambio, se suman de otra manera: mediante la concatenación. Multiplicar una cadena por otra cadena no tiene sentido y no está permitido: + +```python +# No se puede: 'mamá' * 'cuaderno' +``` + +## Los números y las cadenas son de tipos distintos + +Un ejemplo de salida de un número: ```python print(5) # => 5 +print(-5) # => -5 ``` -El número `5` y la cadena de texto `'5'` son cosas completamente diferentes, aunque la salida de `print()` para estos datos es idéntica. Los números enteros (`1`, `34`, `-19`, etc.) y los números racionales (`1.3`, `1.0`, `-14.324`, etc.) son dos tipos de datos diferentes. Esta división está relacionada con las características de los ordenadores. Hay otros tipos, los conoceremos más adelante. +Un ejemplo de salida de una cadena: + +```python +print('5') # => 5 +print('-5') # => -5 +``` + +En la pantalla el resultado se ve igual, pero dentro del programa son cosas completamente distintas: + +| Valor | Tipo de dato | +|----------|-----------------------| +| `5` | `int` (número entero) | +| `'5'` | `str` (cadena) | + +La cadena '5' no se puede sumar con el número 5, igual que '10' / 2 no dará el número 5.0. Python protestará si intentas mezclar tipos incompatibles sin una conversión explícita. -Aquí hay otro ejemplo, pero esta vez con un número racional: +## Números enteros y racionales + +Python distingue dos clases de números: + +- int denota los números enteros: -3, 0, 7, 100000 +- float denota los números reales (racionales): 1.0, -3.14, 2.718 + +Un ejemplo: ```python print(10.234) # => 10.234 +print(-0.4) # => -0.4 +``` + +En ellos se pueden escribir valores fraccionarios y usarlos en cálculos: + +```python +print(3.5 + 1.2) # => 4.7 +print(5.0 / 2.0) # => 2.5 +print(2.75 - 0.5) # => 2.25 +``` + +## Tipos primitivos + +Los tipos como: + +- str (cadena), +- int (número entero), +- float (número racional) + +se llaman primitivos: están incorporados directamente en el lenguaje. + +```text +Tipos primitivos de Python +├── int : números enteros (7, -3, 0) +├── float : números decimales (3.14, -0.5) +├── str : cadenas ('hello') +└── bool : tipo lógico (True, False) ``` -Los tipos de datos "cadena de texto", "número entero" y "número racional" son *tipos primitivos*, es decir, están incorporados en el propio lenguaje Python. El lenguaje también tiene algunos tipos compuestos incorporados, pero por ahora sólo trabajaremos con los primitivos. Los programadores también pueden crear sus propios tipos de datos. +Además de las cadenas y los números, en Python hay un tipo booleano `bool` con los valores `True` y `False`, y también un valor especial, `None`. Con ellos nos encontraremos en detalle más adelante. -En inglés, las cadenas de texto en programación se llaman strings, y las líneas de texto en archivos se llaman lines. Por ejemplo, en el código anterior hay una línea (line), pero no hay cadenas de texto (strings). En el español, a veces puede haber confusión, por lo tanto, en todas las lecciones usaremos **cadena de texto** para referirnos al tipo de datos "cadena de texto", y **línea** para referirnos a las líneas de código (lines) en los archivos. +Existen también los tipos compuestos: listas, diccionarios, tuplas y otros. Los conoceremos más tarde. Más aún, en Python se pueden crear tipos propios (por ejemplo, clases), pero para empezar es importante entender bien los primitivos. diff --git a/modules/33-data-types/45-tuples/es/README.md b/modules/33-data-types/45-tuples/es/README.md index 0dccd7f7..843d1d54 100644 --- a/modules/33-data-types/45-tuples/es/README.md +++ b/modules/33-data-types/45-tuples/es/README.md @@ -1,69 +1,86 @@ -Además de los tipos primitivos, Python tiene tipos de datos compuestos que almacenan varios valores a la vez. Un estudiante universitario se describe con nombre, edad y promedio. Una película tiene título, año de estreno y calificación. Estos grupos de valores es natural almacenarlos juntos. +Además de los primitivos, en Python hay tipos de datos compuestos, que guardan varios valores a la vez. Un estudiante de universidad se describe con el nombre, la edad y la nota media. Una película se describe con el título, el año de estreno y la valoración. Esos grupos de valores resulta cómodo guardarlos juntos, y no como un conjunto de variables separadas. -Una tupla es más simple que cualquier otro tipo compuesto. Almacena varios valores en un orden estrictamente definido. Una vez creada, no se puede modificar. +En Python hay incorporadas varias formas de trabajar con datos compuestos; la más simple de ellas es la tupla (tuple). -Una tupla es adecuada para datos que siempre van juntos. +Imagina que queremos trabajar en el código con el concepto de estudiante, que tiene nombre, edad y nota media. Se puede intentar describir al estudiante con tres variables: ```python -student = ('Alice', 20, 4.8) # nombre, edad, promedio -film = ('Inception', 2010, 8.8) # título, año, calificación +student_name = 'Alice' +student_age = 20 +student_score = 4.8 ``` -## Cómo crear una tupla +¿Y qué pasará si tenemos varios estudiantes? Saldrá un código muy torpe que nadie apreciará. Con las tuplas, en cambio, la vida se vuelve más interesante. Al estudiante se lo puede representar como una tupla. -Una tupla se escribe entre paréntesis con los valores separados por comas. +Una tupla se escribe entre paréntesis, separando los valores con comas: ```python +student = ('Alice', 20, 4.8) # nombre, edad, nota media + point = (10, 20) -colors = ('red', 'green', 'blue') -mixed = (42, 'hello', 3.14) +film = ('Inception', 2010) # título, año +user = ('Andrey Petrov', 'email@example.com', 'hexletcommunity', 100) # nombre, email, telegram, edad ``` -Una tupla de un solo elemento requiere una coma al final. Sin ella, Python interpreta los paréntesis como agrupación de una expresión. +Los paréntesis se pueden omitir. Python determina la tupla por las comas. ```python -single = (42,) # una tupla con un elemento -not_tuple = (42) # simplemente el número 42 -``` +student = 'Alice', 20, 4.8 # nombre, edad, nota media -Los paréntesis son opcionales. Python reconoce una tupla por las comas. - -```python point = 10, 20 -print(type(point)) # => ``` ## Acceso a los elementos -Los elementos de una tupla se numeran desde cero. Se accede a ellos por índice. +Los elementos de una tupla se numeran desde cero. Se accede a ellos por índice, como en las cadenas. ```python -point = (10, 20) -print(point[0]) # => 10 -print(point[1]) # => 20 +student = ('Alice', 20, 4.8) +print(student[0]) # => Alice +print(student[1]) # => 20 +print(student[2]) # => 4.8 ``` ## Las tuplas son inmutables -Una tupla no se puede modificar después de su creación. Intentar reemplazar un elemento produce un error. +Una vez creada, una tupla no se puede modificar. El intento de reemplazar un elemento provoca un error. ```python -point = (10, 20) -point[0] = 5 # TypeError: 'tuple' object does not support item assignment +student = ('Alice', 20, 4.8) +student[0] = 'Bob' # TypeError: 'tuple' object does not support item assignment +``` + +Está hecho a propósito. La tupla se emplea donde un cambio accidental de los datos no es deseable: la configuración de conexión a una base de datos, el registro de un usuario, un catálogo con los días de la semana. + +Si los datos hay que actualizarlos, se crea una tupla nueva y se reasigna la variable. + +```python +student = ('Alice', 20, 4.8) +# usando los datos de la tupla anterior +student = (student[0], student[1] + 1, 4.9) # pasó un año, la nota subió + +print(student) # => ('Alice', 21, 4.9) ``` -La inmutabilidad está incorporada en las tuplas de forma deliberada. Sin importar dónde se pase una tupla, sus datos permanecen iguales. +La tupla antigua queda intacta (pero ya no se puede acceder a ella). La variable simplemente empieza a apuntar a la nueva. ## Desempaquetado Los elementos de una tupla se pueden asignar a varias variables a la vez. ```python -point = (10, 20) -x, y = point +student = ('Alice', 20, 4.8) +name, age, gpa = student -print(x) # => 10 -print(y) # => 20 +print(name) # => Alice +print(age) # => 20 +print(gpa) # => 4.8 ``` -Python asigna los valores a las variables en orden. El número de variables debe coincidir con el número de elementos. +Python empareja los valores con las variables por orden. La cantidad de variables debe coincidir con la cantidad de elementos. + +## Aplicabilidad en la vida real + +Las tuplas en Python aparecen con bastante frecuencia y se usan para guardar juntos varios valores relacionados. Normalmente se aplican en situaciones en las que el conjunto de datos tiene una estructura fija y no debe modificarse después de crearse. Por ejemplo, con una tupla se pueden guardar las coordenadas de un punto (10, 20) o un color (255, 0, 0). Las tuplas se consideran una de las estructuras de datos básicas del lenguaje y se usan con regularidad tanto en Python mismo como en muchas bibliotecas y frameworks. + +Al mismo tiempo, para entidades más complejas, por ejemplo usuarios, pedidos o productos, las tuplas encajan mal, porque en ellas cuesta entender el sentido de cada valor, resulta incómodo guardar una gran cantidad de información y controlar los tipos de datos dentro de la estructura. Para esas tareas, en Python existen herramientas más adecuadas, que conoceremos en Hexlet. diff --git a/modules/33-data-types/50-strong-typing/es/EXERCISE.md b/modules/33-data-types/50-strong-typing/es/EXERCISE.md index d8961e11..2fb78421 100644 --- a/modules/33-data-types/50-strong-typing/es/EXERCISE.md +++ b/modules/33-data-types/50-strong-typing/es/EXERCISE.md @@ -1,2 +1,3 @@ +En un juego hay dos jugadores. El primero consiguió `7` puntos, el segundo perdió puntos: la expresión `(-8 - -2)` describe su resultado. Calcule la diferencia y muéstrela en pantalla: `7 - (-8 - -2)`. -Muestre en pantalla el resultado de la expresión: `7 - (-8 - -2)`. Intente hacer que el número 7 sea una cadena de texto en lugar de un número. ¿Funcionará este código? Experimente también con otros números. +Intente hacer que el número 7 no sea un número, sino una cadena: `'7' - (-8 - -2)`. ¿Funcionará ese código? Experimente también con otros números. diff --git a/modules/33-data-types/50-strong-typing/es/README.md b/modules/33-data-types/50-strong-typing/es/README.md index 35724efb..48440e0d 100644 --- a/modules/33-data-types/50-strong-typing/es/README.md +++ b/modules/33-data-types/50-strong-typing/es/README.md @@ -1,22 +1,77 @@ +Python es un lenguaje de tipado estricto (o fuerte) y se toma los tipos de datos muy en serio. Al intentar ejecutar una operación entre tipos incompatibles, el programa lanzará un error. -Python es uno de los lenguajes que se adhiere estrictamente a los tipos de datos. Por lo tanto, cualquier incompatibilidad de tipos será respondida con un error. Todo esto se debe al tipado fuerte. +## ¿Qué significa eso en la práctica? -Conocemos dos tipos de datos diferentes: números y cadenas de texto. Por ejemplo, podemos sumar números porque la operación de suma es una operación para el tipo "número". Pero, ¿qué sucede si aplicamos esta operación a un número y una cadena de texto? +Supongamos que sumas dos números: ```python -print(1 + '7') # TypeError: unsupported operand type(s)... +print(1 + 7) # => 8 ``` -Python no permitirá sumar el número `1` y la cadena de texto `'7'`, ya que son valores de tipos diferentes. Primero debemos convertir la cadena de texto en un número o el número en una cadena de texto. Hablaremos sobre cómo hacer esto más adelante. +Todo perfecto. La operación de suma está permitida para los números, así que el programa funcionará y mostrará en pantalla *8*. ¿Y qué pasará si intentamos sumar un número y una cadena? -Este enfoque meticuloso hacia la compatibilidad de tipos se llama **tipado estricto** o **tipado fuerte**. Python es un lenguaje con tipado fuerte. +```python +print(1 + '7') +# TypeError: unsupported operand type(s) for +: 'int' and 'str' +``` + +Python informa de que no puede sumar int y str. Es un error de tipos: los valores pertenecen a categorías distintas y el programa se niega a continuar. + +```text +'2' + 1 + +str + int → TypeError! + +Hace falta una conversión: +int('2') + 1 → 3 +'2' + str(1) → '21' +``` + +## ¿Por qué tan estricto? + +Python te protege de errores poco evidentes y peligrosos. Exige que indiques explícitamente cómo quieres convertir los datos: + +- o convertir la cadena en número (`int('7')`); +- o convertir el número en cadena (`str(1)`). + +Aprenderemos a hacerlo un poco más adelante. + +## Cuándo Python convierte los tipos por sí mismo + +Si en una misma expresión aparecen un número entero y uno real, Python convierte el entero a real automáticamente. + +```python +print(1 + 1.5) # => 2.5 +``` + +El entero `1` se convierte en `1.0` y el resultado sale `2.5`. Funciona porque cualquier número entero se representa exactamente como real: los datos no se pierden. Python lo sabe y hace la conversión él mismo (como la mayoría de los demás lenguajes). + +## ¿Y cómo es en otros lenguajes? -No todos los lenguajes funcionan de esta manera. Por ejemplo, PHP es un lenguaje con **tipado débil**. Conoce la existencia de diferentes tipos, pero no se adhiere a su uso de manera estricta. PHP intenta convertir la información cuando parece razonable. Lo mismo ocurre con JavaScript: +No todos los lenguajes son así. Por ejemplo, PHP y JavaScript usan tipado débil. Convierten los tipos automáticamente cuando lo consideran "razonable". ```javascript -// ¿Qué te parece, Elon Musk? -// Número 1 + Cadena de texto 7 = Cadena de texto 17 -1 + '7'; // '17' +1 + '7'; // => '17' +``` + +En este ejemplo, el número 1 se convierte implícitamente en la cadena '1' y el resultado pasa a ser '17'. + +Ese comportamiento apareció por razones bastante objetivas. En muchos lenguajes, sobre todo en los que se usan a menudo en desarrollo web, el programa tiene que trabajar constantemente con datos que vienen de fuera. Por ejemplo, los valores de los formularios HTML, de los parámetros de la URL o de las peticiones HTTP llegan a menudo como cadenas, aunque por su sentido sean números. En PHP, por ejemplo, una cadena con un número puede participar automáticamente en la aritmética como número: + +```php +'7' + 1; // 8 ``` -Por un lado, la conversión automática e implícita de tipos puede parecer conveniente. Pero en la práctica, esta característica del lenguaje crea muchos errores y problemas difíciles de encontrar. El código puede funcionar a veces y otras veces no, dependiendo de si la conversión automática tuvo "suerte". El programador no se dará cuenta de esto de inmediato y perderá mucho tiempo depurando. +Hay también otros lenguajes con sus propias reglas. Por ejemplo, en Ruby el comportamiento en este punto se parece más al de Python: la expresión `1 + '7'` no se considera admisible. Y el lenguaje C también permite algunas conversiones implícitas, pero ya por sus propias reglas, más de bajo nivel. En la práctica, sin embargo, esto lleva a tales problemas que los lenguajes modernos han renunciado a esas licencias. Esos errores son especialmente difíciles de detectar, porque el comportamiento del programa depende del tipo de datos que le llegaron. A veces todo funciona y a veces no. + +Además, los lenguajes no se dividen exactamente en dos bandos: "estrictos" y "débiles". Es más correcto decir que los distintos lenguajes tienen distintos grados de rigor. En unos las conversiones implícitas casi no existen y en otros son bastante más numerosas. + +## Tipado estático y dinámico + +Existe otro concepto aparte: el tipado estático y el dinámico. Describe cuándo y cómo se comprueban los tipos en el lenguaje. Python pertenece a los lenguajes de tipado dinámico: aquí los tipos se comprueban durante la ejecución del programa. En los lenguajes de tipado estático esa comprobación suele ocurrir antes, incluso antes de ejecutar el código, en la etapa de compilación. + +El tipado estricto y el débil describen ya otra propiedad del lenguaje: con qué facilidad el lenguaje realiza conversiones implícitas entre tipos distintos. Es importante no confundirlo con el tipado estático y dinámico. Un término responde a la pregunta "cuándo se comprueban los tipos" y el otro a la pregunta "qué ocurrirá si se mezclan tipos distintos sin una conversión explícita". + +## Conclusión + +Python es un lenguaje de tipado estricto. No permite ejecutar operaciones entre tipos incompatibles sin una conversión explícita. Eso hace que los programas sean más fiables y más claros. Todavía aprenderemos a convertir los datos de un tipo a otro y a hacerlo siempre de forma consciente. diff --git a/modules/33-data-types/52-data-types-immutability/es/README.md b/modules/33-data-types/52-data-types-immutability/es/README.md index f144bf47..f027f331 100644 --- a/modules/33-data-types/52-data-types-immutability/es/README.md +++ b/modules/33-data-types/52-data-types-immutability/es/README.md @@ -1,20 +1,65 @@ - -Supongamos que queremos cambiar un carácter en una cadena de texto. Esto es lo que sucede: +Imaginemos que tenemos una cadena y queremos reemplazar en ella un carácter, por ejemplo la primera letra del nombre: ```python first_name = 'Alexander' -first_name[0] = 'B' -# Error: TypeError: 'str' object does not support item assignment +first_name[0] = 'B' # +``` + +Python lanzará un error de inmediato: + +```text +TypeError: 'str' object does not support item assignment ``` -Esto ocurre debido a la inmutabilidad de los tipos primitivos en Python: el lenguaje no permite cambiar una cadena de texto físicamente. La inmutabilidad de los tipos primitivos es importante por varias razones. La razón principal es el rendimiento. +¿Qué significa? Las cadenas en Python no se pueden modificar por partes. Una vez creadas, pasan a ser inmutables (o, como dicen los programadores, inmutables por diseño). + +## ¿Por qué las cadenas son inmutables? + +Python está hecho de tal manera que los tipos primitivos (cadenas, números, valores booleanos) no se pueden modificar. Eso aporta ventajas importantes: -Pero a veces necesitamos cambiar una cadena de texto. Para eso existen las variables: +- Seguridad: los valores no se modificarán por accidente +- Rendimiento: con objetos inmutables es más fácil trabajar dentro del intérprete +- Previsibilidad: hay menos efectos inesperados al pasar los datos a las funciones + +## ¿Y cómo se modifica entonces una cadena? + +Si hace falta "modificar" una cadena, se crea una cadena nueva y se guarda en la misma variable: ```python first_name = 'Alexander' first_name = 'Blexander' + print(first_name) # => Blexander ``` -Hay una gran diferencia entre *cambiar el valor de una variable* y *cambiar el valor en sí*. No se puede cambiar un tipo primitivo en Python, pero se puede cambiar un tipo compuesto. También se puede reemplazar el valor de una variable sin problemas. +La variable first_name ahora simplemente apunta a otra cadena. Se produce un reemplazo del valor de la variable, y la cadena en sí queda intacta. + +```text +name = 'Alexander' + +name[0] = 'B' → ¡Error! Las cadenas son inmutables. + +name = 'B' + name[1:] +└─────────┬──────────┘ + 'Blexander' ← se creó una cadena nueva +``` + +## ¿Una variable nueva o la misma? + +Reutilizar una variable solo vale la pena cuando se trata de una misma entidad. Si el usuario introdujo un nombre nuevo, esa es ya otra entidad, y es mejor crear una variable aparte: + +```python +# Una misma cadena, simplemente la actualizamos +name = 'Alexander' +name = 'Blexander' + +# Entidades distintas, mejor variables distintas +first_name = 'Alexander' +corrected_first_name = 'Blexander' +``` + +Intentar "meter" todas las cadenas en una sola variable enreda el código. La variable deja de decir qué se guarda en ella. + +## Conclusión + +Los tipos de datos primitivos en Python (las cadenas str, los números enteros int, los números reales float y los valores lógicos bool) son inmutables (immutable). Eso significa que, una vez creados, su valor interno no se puede modificar. Cambiar un carácter de una cadena o un dígito de un número es imposible: cualquier "modificación" ocurre creando un valor nuevo y redefiniendo la variable. diff --git a/modules/33-data-types/55-data-types-casting/es/README.md b/modules/33-data-types/55-data-types-casting/es/README.md index a6471f3b..caaf3a0e 100644 --- a/modules/33-data-types/55-data-types-casting/es/README.md +++ b/modules/33-data-types/55-data-types-casting/es/README.md @@ -1,53 +1,60 @@ +En los programas reales surge a menudo la situación en la que hay que convertir datos de un tipo en otro. Es especialmente habitual, por ejemplo, al procesar la entrada del usuario o los datos de los formularios web. Ahí todo llega en forma de cadenas, incluso si introdujiste un número. -En programación, a menudo nos encontramos con situaciones en las que es necesario convertir un tipo de dato en otro, por ejemplo, al trabajar con formularios en sitios web. Los datos de los formularios siempre llegan en formato de texto, incluso si el valor es un número. Así como vemos a continuación, se puede realizar la conversión: +![Conversión de tipos en Python](./assets/number-as-string.png) + +Para trabajar con esos valores hay que convertirlos explícitamente al tipo necesario, y para eso Python tiene su propio conjunto de funciones. + +## Conversión de cadena a número + +Imaginemos que recibimos del formulario la cadena '345' y necesitamos sumar ese número con otro: ```python -# str se convierte en int number = int('345') -print(number) # => 345 +print(number + 5) # => 350 ``` -`int()` es una función a la que se le pasa un valor para convertirlo. La función se comporta de manera similar a las operaciones aritméticas, pero realiza acciones especiales. Aquí hay algunos ejemplos más: +La función `int()` recibe una cadena y la convierte en un número entero. +Esa función se llama función de conversión de tipo (casting function). ```python value = '0' -# Dentro de los paréntesis se puede especificar una variable converted_value = int(value) print(converted_value) # => 0 -# O un valor específico -converted_value2 = int('10') -print(converted_value2) # => 10 - -converted_value3 = int(False) -print(converted_value3) # => 0 - -converted_value4 = int(True) -print(converted_value4) # => 1 +print(int('10')) # => 10 +print(int(3.5)) # => 3 (la parte decimal se descarta) +``` -# Si se convierte un número de punto flotante -# se descarta la parte decimal -converted_value5 = int(3.5) -print(converted_value5) # => 3 +```text +'123' ──int()──→ 123 ──float()──→ 123.0 + │ + str() + ↓ + '123' ``` -De la misma manera, se pueden convertir datos a cadenas de texto con `str()` y a números de punto flotante con `float()`: +## Conversión a cadena con str() + +Si hace falta convertir un número o un valor lógico en cadena, usa la función `str()`: ```python -value = str(10) -print(value) # '10' +print(str(10)) # => '10' +print(str(True)) # => 'True' +print(str(3.5)) # => '3.5' +``` -value2 = str(True) -print(value2) # 'True' +Esto es útil, por ejemplo, al formar textos, mensajes y salidas: -value3 = float(5) -print(value3) # 5.0 +```python +age = 42 +print("Age: " + str(age)) # => Age: 42 ``` -Python realiza algunas conversiones automáticamente. Por ejemplo, en operaciones donde se encuentran simultáneamente un número entero y un número de punto flotante, Python los convierte automáticamente a **float**: +## Conversión a número de coma flotante con float() + +Si hace falta un número con punto decimal, usa `float()`: ```python -# El código float(3) + 1.2 se ejecuta implícitamente -value = 3 + 1.2 -print(value) # => 4.2 +print(float(5)) # => 5.0 +print(float('2.7')) # => 2.7 ``` diff --git a/modules/33-data-types/55-data-types-casting/es/assets/number-as-string.png b/modules/33-data-types/55-data-types-casting/es/assets/number-as-string.png new file mode 100644 index 00000000..76d69af3 Binary files /dev/null and b/modules/33-data-types/55-data-types-casting/es/assets/number-as-string.png differ diff --git a/modules/35-calling-functions/100-call/es/EXERCISE.md b/modules/35-calling-functions/100-call/es/EXERCISE.md index 788c7de7..84c84ef7 100644 --- a/modules/35-calling-functions/100-call/es/EXERCISE.md +++ b/modules/35-calling-functions/100-call/es/EXERCISE.md @@ -1,2 +1 @@ - -En el código del programa se han definido dos variables que contienen nombres de empresas. Calcula la longitud total de los nombres en caracteres y muéstrala en pantalla. +El sitio comprueba si la publicidad de dos empresas asociadas cabe en una sola línea. En el código están definidos sus nombres: calcula la cantidad total de caracteres de ambos nombres con la función `len()` y muéstrala en pantalla. diff --git a/modules/35-calling-functions/100-call/es/README.md b/modules/35-calling-functions/100-call/es/README.md index 7130054a..be2e5071 100644 --- a/modules/35-calling-functions/100-call/es/README.md +++ b/modules/35-calling-functions/100-call/es/README.md @@ -1,33 +1,133 @@ +La programación sirve para ejecutar las operaciones más diversas. A veces son acciones simples, por ejemplo sumar números o unir cadenas. Pero más a menudo son procesos complejos, como transferir dinero de una cuenta a otra, tramitar un pedido en una tienda en línea, calcular impuestos o preparar un informe. -La suma, la concatenación, el cálculo del resto de la división y otras operaciones que hemos visto son capacidades básicas de los lenguajes de programación. Las matemáticas no se limitan a la aritmética; también hay otras ramas con sus propias operaciones, como la geometría. Lo mismo ocurre con las cadenas de texto: se pueden invertir, cambiar la capitulación de las letras, eliminar caracteres innecesarios, y eso es sólo lo más básico. +Esas operaciones no se pueden expresar con una sola instrucción. Detrás de una acción como "transferir dinero" pueden esconderse decenas, cientos e incluso miles de líneas de código. Es la comprobación del saldo, el cargo del importe, el cálculo de la comisión, la actualización de la base de datos, el envío de la notificación al usuario. -Además, a un nivel más alto y profundo, existe la lógica aplicada a una aplicación específica. Los programas pueden realizar transacciones monetarias, calcular impuestos, generar informes. La cantidad de operaciones similares es infinita y depende de cada programa en particular. Y es necesario saber cómo expresarlas en código. +Para gobernar ese código y no perderse en los detalles se usan las funciones. Una función agrupa un bloque de código en un todo único, oculta la implementación y permite concentrarse en el sentido. Al programador le basta con llamar a la función y confiarle todo el trabajo interno. -Para expresar cualquier operación arbitraria, en programación existe el concepto de **función**. Las funciones pueden ser incorporadas o creadas por el programador. Ya estamos familiarizados con una función incorporada: `print()`. +![Función](./assets/function.png) -Las funciones son una de las construcciones clave en programación. Sin ellas, prácticamente no se puede hacer nada. Es importante familiarizarse con ellas lo antes posible, ya que el resto del material se basa en el uso de funciones al máximo. Primero aprenderemos a utilizar las funciones ya creadas y luego aprenderemos a crear nuestras propias funciones. +Imaginemos una función que transfiere dinero de una cuenta a otra. En realidad, dentro de ella puede haber cientos de líneas de código, pero nosotros no lo vemos. Por fuera todo se ve como una única instrucción simple: -Comenzaremos con funciones sencillas para trabajar con cadenas de texto. +```python +transfer_money('Alice', 'Bob', 100) +``` + +Esa línea llama a la función `transfer_money()`. Se le pasan el remitente `Alice`, el destinatario `Bob` y el importe `100`. + +Aquí hay algunos ejemplos más de llamadas a funciones que podríamos implementar. Cada función tiene su nombre y su propio conjunto de datos para trabajar. + +```python +# Sí, sí, print también es una función +print('Hexlet!') + +# Envío de un correo al usuario +send_email('bob@example.com', 'Bienvenido!') + +# Cálculo del impuesto sobre el importe indicado +calculate_tax(5000, 'Florida') + +# Comprobación de si el usuario está en el sistema +is_registered('Alice') + +# Obtención de un número aleatorio del 1 al 10 +random_number(1, 10) + +# Creación de una copia de seguridad de la base de datos +backup_database() + +# Cálculo de la longitud de una cadena +len('Hexlet') # Resultado: 6 +``` + +En la llamada a una función se escribe primero su **nombre** y luego los **paréntesis**. Los paréntesis muestran que se trata precisamente de una llamada. Así entendemos que ante nosotros hay una función y no una variable. + +Dentro de los paréntesis se indican los **argumentos**, es decir, los datos que la función recibe para trabajar. Pueden ser varios, uno o ninguno en absoluto. + +## ¿De dónde salen las funciones? + +Unas funciones están incorporadas en el lenguaje (built-in) y otras las crean los propios programadores. + +Las **funciones incorporadas** son funciones que vienen junto con el lenguaje Python. Se pueden usar de inmediato, sin acciones adicionales. Como ejemplo se puede citar la función `print()`. Como se dice, está disponible globalmente. + +Las **funciones definidas por los programadores** se crean cuando hace falta encapsular la lógica propia en un bloque aparte. A esa función se le puede poner cualquier nombre y usarla en el código igual que las incorporadas. Aprenderemos a hacerlo más adelante. + +Además, existen funciones que se encuentran en bibliotecas aparte. Para usarlas hay que conectarlas mediante el mecanismo de importación. La importación no la analizamos todavía en detalle. Por ahora basta con saber que permite conectar un conjunto externo de funciones y hacerlas accesibles en el programa. + +## Función con un parámetro + +Una de las funciones incorporadas que se usan con más frecuencia es `len()`. Para una cadena devuelve la cantidad de caracteres. -La función `len()` cuenta la cantidad de caracteres en una cadena de texto. A continuación, se muestra un ejemplo de su llamado: +```python +message = 'Hello!' +count = len(message) +print(count) # => 6 +``` + +Aquí la cadena `'Hello!'` tiene seis caracteres, por eso la llamada `len(message)` devolverá el número `6`. + +```text +Argumentos Función Resultado +┌──────────┐ ┌──────────┐ ┌──────────┐ +│ 'Hello!' │ ──→ │ len() │ ──→ │ 6 │ +└──────────┘ └──────────┘ └──────────┘ +``` + +## Devolución del valor + +La devolución del valor es uno de los principios clave del funcionamiento de las funciones. Gracias a ella podemos unir los resultados de distintas acciones y construir una lógica más compleja. Si una función devuelve un valor, podemos guardarlo en una variable, pasarlo a otra función o usarlo en cálculos. Así funciona precisamente `len()`. Cuenta la cantidad de elementos y entrega el resultado hacia fuera. + +```python +message1 = 'Hello!' +length1 = len(message1) # guardamos el resultado + +message2 = 'World!' +length2 = len(message2) + +combined_length = length1 + length2 # usamos el resultado en una expresión +print(combined_length) # 12 +``` + +Si `len()` imprimiera el resultado en pantalla de inmediato (como hace `print()`), veríamos el número, pero no podríamos usarlo: ```python -# Ejecución de la función len con el parámetro 'Hello!' -result = len('Hello!') -print(result) # => 6 +# función imaginaria que solo imprime el resultado +fake_len('Hello!') # imprimirá 6 + +# pero más adelante ese número ya no está disponible +# no podemos sumarlo, guardarlo ni compararlo +result = fake_len('Hello!') # aquí en result no hay nada ``` -**Parámetros o argumentos** son la información que una función recibe al ser ejecutada. Con base en esta información, la función generalmente realiza un cálculo y devuelve un resultado. +Por eso la devolución del valor es un concepto tan importante. Permite enlazar las funciones entre sí. Unas devuelven datos y otras los usan en su trabajo. Así es precisamente como, a partir de pasos pequeños, se construyen programas grandes y complejos. -Hemos creado la variable `resultado` y le hemos indicado al intérprete la acción específica: guardar en ella el resultado que devuelve la función `len()` al ser llamada. En este sentido, las funciones son similares a las operaciones, ya que siempre devuelven un resultado de su trabajo. La expresión `len('Hello!')` significa que se ejecuta la función con el nombre *len*, y se le pasa el parámetro `'Hello!'`. La función `len()` cuenta la longitud de la cadena de texto que se le ha pasado. +## Función con varios parámetros -La ejecución de una función siempre se indica con paréntesis `()`, que van justo después del nombre de la función. Entre los paréntesis puede haber cualquier cantidad de parámetros, o incluso ninguno. La cantidad depende de la función que se esté utilizando. +Algunas funciones reciben a la vez varios datos para trabajar. Como ejemplo sirve la función incorporada `pow()`, que eleva un número a la potencia necesaria. El primer parámetro recibe la base de la potencia y el segundo fija el exponente. -Tomemos como ejemplo la función `pow()`, que eleva un número dado a una potencia determinada. Esta función recibe dos parámetros: toma el primer parámetro y lo eleva a la potencia que se pasa como segundo parámetro: +```python +# elevamos 2 a la 3.ª potencia: 2 * 2 * 2 +result = pow(2, 3) +print(result) # => 8 + +# 5 a la 2.ª potencia: 5 * 5 +print(pow(5, 2)) # => 25 +``` + +Por su estructura, una llamada con varios parámetros no se diferencia de una llamada con uno. El mismo nombre de la función, los paréntesis y los argumentos separados por comas dentro. + +## Parámetros y argumentos + +En las conversaciones sobre funciones aparecen a cada rato las palabras **parámetros** y **argumentos**. Están relacionadas, pero no son lo mismo. + +De **parámetros** se habla al crear la función. Se llama parámetro a la variable dentro de la función en la que cae el valor pasado. De **argumentos** se habla al llamarla. Se llama argumento a lo que pasamos a la función. Puede ser un número, una variable o cualquier expresión. ```python -result = pow(2, 3) # 2 * 2 * 2 -print(result) # => 8 +# números como argumentos +print(pow(2, 3)) # => 8 + +x = 2 +# el argumento puede ser una expresión: se evaluará antes de pasarla a la función +print(pow(x + 1, 3)) # => 27 ``` -Hemos aprendido cómo utilizar funciones incorporadas sencillas. Pero esta no es una lista exhaustiva de funciones. Aprenderán más sobre funciones en las próximas lecciones. +Memorizarlo no es obligatorio, pero servirá al leer literatura en inglés. diff --git a/modules/35-calling-functions/100-call/es/assets/function.png b/modules/35-calling-functions/100-call/es/assets/function.png new file mode 100644 index 00000000..f89fee08 Binary files /dev/null and b/modules/35-calling-functions/100-call/es/assets/function.png differ diff --git a/modules/35-calling-functions/135-calling-functions-default-params/es/README.md b/modules/35-calling-functions/135-calling-functions-default-params/es/README.md index 38363d45..1a73a287 100644 --- a/modules/35-calling-functions/135-calling-functions-default-params/es/README.md +++ b/modules/35-calling-functions/135-calling-functions-default-params/es/README.md @@ -1,26 +1,57 @@ +Algunas funciones en Python tienen **parámetros opcionales**. Eso significa que para ellos hay fijado de antemano un valor por defecto, y al llamar a la función se puede no indicar ese parámetro. -Veamos la función `round()`, que redondea un número que se le pasa como argumento: +![Taller mecánico](./assets/default-parameters.png) + +Veamos la función incorporada `round()`, que redondea un número: ```python result = round(10.25, 0) # 10.0 ``` -Le pasamos dos parámetros: +Le pasamos dos valores: -* El número que queremos redondear -* La precisión de redondeo +- El número que hay que redondear. +- La precisión del redondeo; `0` significa que el redondeo será hasta el número entero. -El valor `0` significa que se redondeará al número entero más cercano. En la mayoría de los casos, queremos redondear al número entero y no a los decimales. Por eso, los creadores de la función `round` hicieron el segundo parámetro **opcional** y le dieron un **valor por defecto de `0`**. Esto significa que podemos no especificar el segundo parámetro y el resultado será el mismo: +Como lo que se necesita con más frecuencia es precisamente el redondeo al entero, los creadores de la función `round()` hicieron el segundo parámetro opcional y le pusieron el valor por defecto `0`. Por eso el resultado será el mismo incluso si no se indica el segundo parámetro: ```python result = round(10.25) # 10.0 ``` -Y si queremos una precisión diferente, podemos pasar el parámetro: +Si hace falta otra precisión, se puede indicar explícitamente: ```python # redondeo a un decimal result = round(10.25, 1) # 10.2 ``` -Si una función en Python tiene argumentos opcionales, siempre se colocan después de los argumentos obligatorios. Pueden ser cualquier cantidad. Esto depende de la función en sí, pero siempre van juntos y al final de la lista de argumentos. +```text +round(10.25, 1) → argumentos: 10.25, 1 → 10.3 +round(10.25) → argumentos: 10.25, (0) → 10 + └── valor por defecto +``` + +La cantidad de parámetros opcionales depende de la función concreta, pero los obligatorios van siempre antes de los opcionales. + +## La firma de la función + +Cada función tiene una **firma**, que contiene la descripción de su nombre, sus parámetros y el orden en que se usan. La firma ayuda a entender qué datos espera la función y qué ocurrirá si no se indican los parámetros. + +Miremos la [documentación](https://docs.python.org/3/library/functions.html#round) de la función `round()`. + +```python +round(number, ndigits=None) +``` + +Esa es precisamente la firma. La función se llama `round`. El parámetro `number` es obligatorio: recibe el número a redondear. El parámetro `ndigits` tiene el valor por defecto `None`, es decir, es opcional; si no se indica, el redondeo se hará hasta el número entero. + +## Cómo trabajar con funciones nuevas + +Cuando te encuentras con una función nueva, puedes usar un patrón simple: + +1. Abrir la documentación y encontrar la firma de la función. +2. Mirar los ejemplos de uso. +3. Pasar al intérprete interactivo de Python (REPL) e intentar llamar a la función con distintos argumentos. + +Ese enfoque ayuda a entender rápido cómo funciona exactamente la función, cuáles son sus parámetros obligatorios y opcionales y qué resultados devuelve. diff --git a/modules/35-calling-functions/135-calling-functions-default-params/es/assets/default-parameters.png b/modules/35-calling-functions/135-calling-functions-default-params/es/assets/default-parameters.png new file mode 100644 index 00000000..97e4297a Binary files /dev/null and b/modules/35-calling-functions/135-calling-functions-default-params/es/assets/default-parameters.png differ diff --git a/modules/35-calling-functions/150-calling-functions-expression/es/README.md b/modules/35-calling-functions/150-calling-functions-expression/es/README.md index 7917842e..a98b193c 100644 --- a/modules/35-calling-functions/150-calling-functions-expression/es/README.md +++ b/modules/35-calling-functions/150-calling-functions-expression/es/README.md @@ -1,55 +1,96 @@ - -Continuamos entendiendo las funciones y sus componentes. Hoy hablaremos sobre las expresiones y si una llamada a una función puede considerarse una expresión. - -En programación, una expresión devuelve un resultado que se puede utilizar. Probablemente ya sepas mucho sobre expresiones y los principios en los que se basan. Por ejemplo, operaciones matemáticas como la suma y la resta, operaciones de cadenas como la concatenación, todas estas son expresiones: +Cuando escribimos programas, necesitamos enlazar las acciones unas con otras. La suma de números, la unión de cadenas y el trabajo con variables son ejemplos de cómo unos pasos simples se pueden combinar para obtener un comportamiento más complejo. ```python -1 + 5 * 3 -'He' + 'Let' -# Las variables pueden ser parte de una expresión -rate * 5 +rate = 10 +hours = 5 +salary = rate * hours + 100 +print(salary) # => 150 ``` -La característica de las expresiones es que devuelven un resultado que se puede utilizar posteriormente, como asignarlo a una variable o imprimirlo en pantalla. Así es como se ve en el código: +En programación, para eso se usa el concepto de **expresión**. Designa una construcción que se evalúa y da un resultado. En el ejemplo de arriba, `rate * hours + 100` es una expresión, compuesta de variables (`rate`, `hours`), un literal numérico (`100`) y operaciones aritméticas. Todo junto devuelve un resultado que se puede guardar en una variable o usar más adelante. + +¿Qué nos aporta ese conocimiento? Entendemos que las expresiones se pueden combinar infinitamente, complicando la lógica poco a poco. Cada nueva expresión pasa a ser parte de una mayor: ```python -# Aquí la expresión es 1 + 5 -sum = 1 + 5 -print(1 + 5) +bonus = 50 +# Expresión formada por muchas operaciones +salary = (rate * hours + bonus) * 12 - 500 +print(salary) ``` -Pero no todo en programación es una expresión. La declaración de una variable es una instrucción y, por lo tanto, no puede ser parte de una expresión. Es decir, este código dará un error: +Aquí varias expresiones se unieron en una y el resultado se volvió aún más complejo. Así es precisamente como se construyen los programas. Los pasos pequeños se suman en construcciones grandes. Por eso en programación es imposible aprender de memoria todas las combinaciones por adelantado. Es mucho más importante entender cómo se enlazan las expresiones entre sí hasta dar el resultado necesario. + +## Las expresiones como argumentos de las funciones + +El argumento de una función es siempre algún valor. Pero el valor no solo se puede escribir directamente, sino también calcular. Y eso significa que en los argumentos de una función se pueden sustituir cualesquiera expresiones. ```python -# Código sin sentido que no funcionará -10 + sum = 1 + 5 +# Aquí el argumento de la función print es el número 150 +print(150) + +# Y aquí el argumento es una expresión, que primero se evalúa +print(10 * 15) # => 150 + +# Se puede combinar de forma aún más compleja +rate = 10 +hours = 15 +bonus = 50 +print(rate * hours + bonus) # => 200 ``` -Ahora veamos si una ejecución de una función se considera una expresión. +La función `print()` simplemente recibe un valor ya listo y lo muestra en pantalla. La forma de obtener ese valor le es indiferente a la función. Precisamente por eso las llamadas a funciones combinan perfectamente con cualquier expresión. + +## Llamada a una función dentro de otra función -Sabemos que las funciones devuelven un resultado, lo que significa que son expresiones. De esto se deduce automáticamente muchas cosas interesantes. Por ejemplo, podemos usar una llamada a una función directamente en operaciones matemáticas. Así es como podemos obtener el índice del último carácter en una palabra: +Como la llamada a una función es en sí misma una expresión, su resultado se puede pasar de inmediato a otra función. Eso permite construir construcciones aún más complejas. ```python name = 'python' -# Los índices comienzan en cero -# ¡Ejecución de una función y resta juntas! -last_index = len(name) - 1 -print(last_index) # => 5 + +# La llamada len(name) devuelve 6 +# Ese resultado se usa de inmediato como argumento de print() +print(len(name)) # => 6 ``` -En este código no hay una nueva sintaxis. Simplemente hemos combinado partes conocidas basándonos en su naturaleza. Podemos ir aún más lejos: +Aquí `len(name)` se evalúa primero y devuelve el número 6. Después ese valor se sustituye en la llamada a `print()`. Esa combinación puede ser tan profunda como se quiera. El resultado de una función se puede pasar a otra y después a la siguiente. -```python -print(len(name) - 1) # => 5 +Para leer correctamente esas construcciones hay que recordar el orden de las llamadas. + +1. Primero se ejecuta la función que está "dentro", en nuestro caso `len(name)`. +2. Después su resultado se sustituye en el lugar de la llamada. +3. A continuación se ejecuta la función externa, en este caso `print()`. + +Así pues, el código `print(len(name))` se puede descomponer mentalmente de esta forma: + +```text +print(len('python')) + +Paso 1: len('python') → 6 +Paso 2: print(6) → muestra 6 ``` -Todo esto es válido para cualquier función, por ejemplo, las funciones de cadenas: +1. `len(name)` → `6` +2. `print(6)` +3. En la pantalla aparece `6`. + +Ese principio funciona siempre. Primero se evalúan las llamadas anidadas y luego la externa. + +## Uso de funciones como parte de expresiones + +Las funciones devuelven valores, y eso significa que sus llamadas se pueden usar como argumentos de otras funciones y como parte de cualquier otra expresión. ```python name = 'python' -# Se utiliza interpolación -print(f'Último carácter: {name[len(name) - 1]}') -# 'Último carácter: n' + +# La llamada len(name) devuelve 6 +# Restamos 1 para obtener el índice del último carácter +last_index = len(name) - 1 +print(last_index) # => 5 + +# Se puede usar el resultado de la función en la aritmética +text = 'hexlet' +double = len(text) * 2 +print(double) # => 12 ``` -Como verás más adelante, las expresiones se pueden combinar para obtener comportamientos más complejos en diferentes lugares y de cualquier manera. Cuanto más profundices en el estudio de Python y practiques, mejor entenderás cómo trabajar con expresiones. Con el tiempo, aprenderás cómo combinar partes de código para obtener el resultado deseado. +Aquí las llamadas `len(name)` y `len(text)` son expresiones de pleno derecho. Devuelven valores que se pueden combinar con números, variables y otras operaciones. diff --git a/modules/35-calling-functions/180-variadic-parameters/es/EXERCISE.md b/modules/35-calling-functions/180-variadic-parameters/es/EXERCISE.md index 03cee9a9..e98d59f1 100644 --- a/modules/35-calling-functions/180-variadic-parameters/es/EXERCISE.md +++ b/modules/35-calling-functions/180-variadic-parameters/es/EXERCISE.md @@ -1,2 +1 @@ - -Calcule programáticamente (no mentalmente) el número mínimo entre 3, 10, 22, -3, 0, y muéstrelo en pantalla. Utilice la función `min()`, que funciona de manera similar a `max()`. +Durante una semana se registraron estas temperaturas del aire: 3, 10, 22, -3, 0 grados. Encuentra el valor mínimo con la función `min()` y muéstralo en pantalla. diff --git a/modules/35-calling-functions/180-variadic-parameters/es/README.md b/modules/35-calling-functions/180-variadic-parameters/es/README.md index ae713703..6f738f04 100644 --- a/modules/35-calling-functions/180-variadic-parameters/es/README.md +++ b/modules/35-calling-functions/180-variadic-parameters/es/README.md @@ -1,22 +1,33 @@ +Algunas funciones tienen una particularidad: reciben un número variable de argumentos. No está relacionado con los valores por defecto, como en el caso de `round()`. Se trata de que la cantidad de datos que se pasan no está limitada a un número fijo. -En esta lección vamos a analizar la función `max()`, y veremos cómo funciona en Python. - -Algunas funciones tienen la particularidad de aceptar un número variable de parámetros. Y no nos referimos a los valores por defecto. Mira este ejemplo: +Veamos la función `max()`. Encuentra el mayor valor entre los datos que se le pasan. ```python -max(1, 10, 3) # 10 +print(max(1, 10, 3)) # => 10 +print(max(1, -3, 2, 3, 2)) # => 3 ``` -En el ejemplo anterior, la función `max()` encuentra el valor máximo entre los parámetros proporcionados. Para saber cuántos parámetros se pueden pasar como entrada, debemos consultar la [documentación](https://docs.python.org/3/library/functions.html?highlight=pow#max) de esta función. Allí veremos esta construcción: +En la documentación se pueden encontrar varias variantes de descripción de `max()`. Para simplificar la comprensión, nos centraremos en una, la que necesitamos ahora: ```python -max(arg1, arg2, *args[, key]) +max(arg1, arg2, /, *args, key=None) ``` -Esto significa que `max()` acepta dos o más parámetros: +Eso significa: + +- la función exige como mínimo dos valores (`arg1` y `arg2`); +- después se pueden pasar tantos valores más como se quiera (`*args`); +- la función devolverá el mayor de los valores pasados. + +Si entre los argumentos hay varios valores máximos iguales, se devuelve el primero de ellos. ```python -max(1, -3, 2, 3, 2) # 3 +print(max(5, 5, 2)) # => 5 ``` -Si la función encuentra varios parámetros con el valor máximo, devolverá el primero de estos. +De forma análoga funciona la función `min()`, solo que busca el menor valor: + +```python +print(min(1, 10, 3)) # => 1 +print(min(1, -3, 2, 3, 2)) # => -3 +``` diff --git a/modules/35-calling-functions/270-deterministic/es/README.md b/modules/35-calling-functions/270-deterministic/es/README.md index 38f63a26..04bb2a37 100644 --- a/modules/35-calling-functions/270-deterministic/es/README.md +++ b/modules/35-calling-functions/270-deterministic/es/README.md @@ -1,7 +1,8 @@ +Las funciones, en cualquier lenguaje de programación, tienen propiedades fundamentales. Esas propiedades ayudan a entender cómo se comportará la función en distintas situaciones, cómo probarla y dónde aplicarla. Una de esas propiedades es el **determinismo**. -Las funciones en cada lenguaje de programación tienen propiedades fundamentales. Estas propiedades ayudan a predecir el comportamiento de las funciones, las formas de probarlas y dónde utilizarlas. Una de estas propiedades es el determinismo. +![Dos personas sentadas hablando de una función determinista](./assets/deterministic.png) -Una función determinista devuelve el mismo resultado para los mismos parámetros de entrada. Por ejemplo, se puede considerar determinista una función que cuenta la cantidad de caracteres: +Una **función determinista** devuelve siempre el mismo resultado con los mismos datos de entrada. Por ejemplo, determinista se puede llamar a la función que cuenta la cantidad de caracteres: ```python len('hexlet') # 6 @@ -11,12 +12,14 @@ len('wow') # 3 len('wow') # 3 ``` -Se puede ejecutar esta función infinitamente y pasarle el valor `'hexlet'` - siempre devolverá `6`. +A la función `len()` se la puede llamar infinitamente con el mismo argumento, y siempre devolverá el mismo resultado. -Veamos ahora el caso contrario: las funciones no deterministas. Por ejemplo, una función que devuelve un número aleatorio pertenece a esta categoría: para una misma entrada, siempre obtendremos un resultado diferente. Si al menos una de cada millón de llamadas a la función devuelve un resultado diferente, se considera no determinista. Esto también se aplica cuando no se toman parámetros: +## Funciones no deterministas + +Al tipo opuesto pertenecen las **funciones no deterministas**. Pueden devolver resultados distintos con los mismos datos de entrada o sin ellos (funciones sin parámetros). Un buen ejemplo es la función que devuelve un número aleatorio: ```python -# La sintaxis de las importaciones se estudiará más adelante en Hexlet +# La sintaxis de las importaciones se estudiará más adelante from random import random # Función que devuelve un número aleatorio @@ -24,4 +27,21 @@ random() # 0.09856613113197676 random() # 0.8839904367241888 ``` -El determinismo es una propiedad importante de una función, ya que afecta a muchos aspectos. Por ejemplo, las funciones deterministas son convenientes para trabajar, ya que son fáciles de optimizar y probar. Si es posible, es mejor hacer que una función sea determinista. +Esa función no tiene argumentos, pero su resultado es distinto cada vez. Si al menos una llamada entre millones da otro resultado, la función se considera no determinista. + +```text +Determinista: No determinista: +len('abc') → siempre 3 random() → 0.42 +len('abc') → siempre 3 random() → 0.91 +len('abc') → siempre 3 random() → 0.07 +``` + +## Por qué esto importa + +El determinismo influye en cómo trabajamos con las funciones. + +- las funciones deterministas son fáciles de probar y de predecir; +- son más simples de optimizar y de reutilizar; +- las funciones no deterministas son más difíciles de comprobar, porque el resultado cambia. + +Por eso, allí donde sea posible, es mejor aspirar a que la función sea determinista. diff --git a/modules/35-calling-functions/270-deterministic/es/assets/deterministic.png b/modules/35-calling-functions/270-deterministic/es/assets/deterministic.png new file mode 100644 index 00000000..1a6cf820 Binary files /dev/null and b/modules/35-calling-functions/270-deterministic/es/assets/deterministic.png differ diff --git a/modules/38-objects/100-objects/es/EXERCISE.md b/modules/38-objects/100-objects/es/EXERCISE.md index 82ff56bf..6e49b833 100644 --- a/modules/38-objects/100-objects/es/EXERCISE.md +++ b/modules/38-objects/100-objects/es/EXERCISE.md @@ -1,2 +1 @@ - -Convierte la cadena `text` a minúsculas e imprímela en la pantalla. En la teoría se muestra un ejemplo de un método que realiza esta tarea. +El usuario escribió el texto con el bloqueo de mayúsculas activado: las letras quedaron en distintos casos. Antes de guardarlo, el programa pasa el texto a minúsculas. Convierte la cadena de la variable `text` a minúsculas y muéstrala en pantalla. En la teoría se muestra un ejemplo del método que resuelve esta tarea. diff --git a/modules/38-objects/100-objects/es/README.md b/modules/38-objects/100-objects/es/README.md index ae719048..f01880a5 100644 --- a/modules/38-objects/100-objects/es/README.md +++ b/modules/38-objects/100-objects/es/README.md @@ -1,83 +1,83 @@ +Python admite la programación orientada a objetos (POO): de forma muy simplificada, es un enfoque en el que operamos no con datos y funciones, sino con objetos y métodos. No pensamos detenernos en detalle en este tema en este curso, porque es amplio y su comprensión requiere cierto nivel de preparación. Ignorarlo por completo es imposible, porque los objetos aparecen literalmente de inmediato, en cuanto empezamos a escribir código en Python. Por eso tocaremos este tema, pero solo en la medida necesaria para las tareas actuales. -Los datos con los que operamos en los programas tienen atributos importantes. En Python, estos atributos están incorporados en el lenguaje. Además, los datos tienen métodos, que son funciones dentro de las propiedades. Las propiedades y los métodos son expresiones similares a las variables o ejecuciones de funciones. Todo esto se puede combinar de diferentes maneras. En esta lección, aprenderemos los fundamentos. +![Upper](./assets/upper.png) -En programación, operamos con datos, creamos números y cadenas, realizamos diversas operaciones sobre ellos y utilizamos el resultado obtenido. Para realizar una operación, aplicamos operadores o funciones: +Hasta este momento trabajábamos en el código con datos y les aplicábamos funciones. En la POO, en lugar de datos tenemos objetos sobre los que se llaman métodos. Por ejemplo, las cadenas en Python son objetos y tienen un método `upper()`, que convierte todas las letras a mayúsculas. ```python -# Suma utilizando el operador + -1 + 3 # 4 - -# Contar la longitud utilizando la función len() -name = 'Hexlet' -len(name) # 6 +text = 'hexlet' +print(text.upper()) # => HEXLET ``` -En el ejemplo anterior, hay una clara separación entre los datos y las funciones. Pero esta no es la única forma de organizar el código. En Python, además de esta separación, se utiliza otro enfoque: **orientado a objetos (OO)**. +A diferencia de las funciones, los métodos se llaman *sobre un objeto*. Primero se escribe el objeto y luego, después de un punto, la llamada al método. A pesar de que el método `upper()` no recibe argumentos, por dentro sabe sobre qué objeto se lo llama y tiene acceso al objeto mismo. -El código orientado a objetos se basa en combinar datos y funciones en una sola entidad: un **objeto**. En este caso, los datos se llaman atributos y las funciones se llaman métodos. +Surge entonces la pregunta lógica: ¿por qué `len()` está implementada como una función normal y no como un método `str.len()`? Ocurre que `len()` no trabaja solo con cadenas: es una función universal que se puede aplicar a multitud de objetos distintos. A usar objetos y a crear tipos de objetos propios aprendemos en los cursos avanzados de Hexlet. -Así es como se ve: +Las cadenas tienen bastantes métodos incorporados; estos son algunos de ellos. ```python -name = 'Hexlet' -# Método upper() -upper_name = name.upper() -print(upper_name) # => 'HEXLET' -``` - -Las cadenas en Python son objetos. En el ejemplo anterior, ejecutamos un método, es decir, a una función que está asociada con la cadena. La llamada se realiza utilizando un punto que va inmediatamente después del nombre de la variable. En todo lo demás, los métodos funcionan como funciones normales. +# Convertir la primera letra a mayúscula +print('hexlet'.capitalize()) # => Hexlet -También se puede hacer la llamada directamente: +# Convertir todas las letras a minúsculas +print('HeXleT'.lower()) # => hexlet -```python -'Hexlet'.upper() # 'HEXLET' +# Eliminar los espacios al principio y al final de la cadena +print(' hi '.strip()) # => hi ``` -Las cadenas tienen muchos métodos incorporados que son constantemente útiles para los desarrolladores. Puede ver una lista de ellos en la [documentación](https://python.readthedocs.io/en/latest/library/stdtypes.html#string-methods). Aquí hay algunos ejemplos útiles: +Algunos métodos reciben parámetros. Por ejemplo, en el método `replace()` el primer parámetro contiene la subcadena que hay que reemplazar y el segundo contiene la cadena de reemplazo. ```python -name = 'Python' +text = 'abracadabra' -# Devuelve el índice de la primera aparición de una letra en una cadena -name.find('t') # 2 +print(text.replace('a', 'o')) # => obrocodobro +print(text.replace('abra', '!')) # => !cad! +``` -# Convierte a minúsculas -name.lower() # 'python' +En Python hay realmente muchos métodos, y no se aprenden de memoria. Normalmente los programadores, en el transcurso del trabajo, recuerdan qué operaciones necesitan en general y cómo se llaman aproximadamente esos métodos. Cuando surge una tarea, o recuerdan el método adecuado, o lo encuentran rápido en la documentación. -# Reemplaza una subcadena por otra -name.replace('on', 'off') # 'Pythoff' -``` +## Método y función: comparación -Lo mismo se aplica a los números y otros tipos de datos que aún no hemos estudiado. Se puede decir que en Python casi todo es un objeto: +Desde el punto de vista del código, los métodos y las funciones se comportan de forma parecida. Reciben valores y devuelven un resultado. Se diferencian solo en la **sintaxis** de la llamada. ```python -x = -5 -# Devuelve el valor absoluto del número -# El nombre parece extraño, pero es el nombre real del método -x.__abs__() +# Llamada a una función +len('hexlet') + +# Llamada a un método +'hexlet'.upper() ``` -En el ejemplo anterior, hay un nombre de método con dos guiones bajos al principio y al final. En Python, se llaman métodos que no están destinados a ser ejecutados directamente. Para ellos, se crearon funciones que, dentro de sí mismas, ejecutan los métodos: +La función se llama desde fuera y recibe el argumento entre paréntesis. El método es una operación incorporada en el valor mismo. Por debajo el valor se pasa hacia dentro como parámetro cero, pero eso queda oculto para nosotros. -```python -x = -5 -abs(x) # ejecuta a x.__abs__() -# -5 elevado a la potencia 3 -pow(x, 3) # ejecuta a x.__pow__(3) +```text +Función: len('hexlet') → 6 + └── argumento + +Método: 'hexlet'.upper() → 'HEXLET' + └── objeto └── método ``` -El creador de Python [decidió](https://stackoverflow.com/questions/83983/why-isnt-the-len-function-inherited-by-dictionaries-and-lists-in-python) que sería más claro expresar las operaciones matemáticas o similares a las matemáticas mediante funciones. Quería que estas funciones se percibieran como operaciones, como la suma o la resta. Esto es más familiar para aquellos que han estudiado matemáticas. +## Los métodos devuelven valores -Lo mismo ocurre con la función `len()`: +Igual que las funciones, los métodos **devuelven un resultado**. Se pueden usar como parte de expresiones. ```python -len('Hexlet') # Llama a 'Hexlet'.__len__() +name = 'hexlet' +print(name.upper() + '!') # => HEXLET! ``` -Además de los métodos, los objetos también tienen atributos, pero los objetos incorporados en Python tienen pocos atributos. Por ejemplo, el atributo `__doc__` devuelve la documentación de una función. Por lo tanto, las funciones también se consideran objetos: +Los métodos de las cadenas siempre devuelven una cadena nueva y dejan la original sin cambios. Ese comportamiento se llama inmutabilidad. Todavía hablaremos de ello más adelante, pero por ahora es importante entender que la cadena queda igual y que el resultado del método es un valor nuevo. ```python -len.__doc__ # 'Return the number of items in a container.' +name = 'hexlet' +print(name.upper()) # => HEXLET +print(name) # => hexlet ``` -Los atributos funcionan y se ven como variables, pero se especifican con un punto después del objeto. +## Para qué hacen falta los métodos en Python + +En Python, parte de las posibilidades está implementada precisamente como métodos. Eso permite agrupar las operaciones junto a los tipos de datos a los que se refieren. Las cadenas tienen un conjunto de métodos, los números otro y las listas un tercero. Así, en el lenguaje coexisten dos formas de trabajo. Las funciones de propósito general se aplican a cualesquiera datos, y los métodos están "adheridos" a tipos concretos. + +Si miramos la POO en su conjunto, aporta algo llamado polimorfismo de subtipos (subtyping), que en este curso no se analiza. diff --git a/modules/38-objects/100-objects/es/assets/upper.png b/modules/38-objects/100-objects/es/assets/upper.png new file mode 100644 index 00000000..5c810856 Binary files /dev/null and b/modules/38-objects/100-objects/es/assets/upper.png differ diff --git a/modules/38-objects/200-methods-immutability/es/README.md b/modules/38-objects/200-methods-immutability/es/README.md index 370b8bfb..d37dde74 100644 --- a/modules/38-objects/200-methods-immutability/es/README.md +++ b/modules/38-objects/200-methods-immutability/es/README.md @@ -1,19 +1,66 @@ +En Python, algunos tipos de datos se llaman **inmutables**. Eso significa que, una vez creados, su contenido no se puede modificar. A esos tipos primitivos pertenecen `int`, `float`, `str` y `bool`. -Supongamos que tenemos la siguiente ejecución: +Veámoslo con el ejemplo de las cadenas. + +### Los métodos de las cadenas no modifican el original + +Cuando llamamos a un método de una cadena, parece que la estamos modificando. Por ejemplo, la pasamos a mayúsculas. + +```python +text = 'hexlet' +text.upper() +print(text) # => hexlet +``` + +En realidad, el método `upper()` **devuelve una cadena nueva** en mayúsculas, y la cadena original `text` queda igual. + +```text +text = 'Python' + +text.upper() → 'PYTHON' (cadena nueva) +text → 'Python' (no cambió) +``` + +Para no perder el resultado, guardémoslo en una variable. ```python -name = 'Tirion' -print(name.upper()) # => TIRION -# ¿Qué imprimirá esta ejecución? -print(name) # => ? +text = 'hexlet' +new_text = text.upper() +print(new_text) # => HEXLET ``` -La llamada al método `.upper()` devuelve un nuevo valor en el que todas las letras están convertidas a mayúsculas, pero no modifica la cadena original. Por lo tanto, el valor dentro de la variable seguirá siendo el antiguo: `'Tirion'`. Esta lógica se aplica a los métodos de todos los tipos primitivos. +Si no guardas el resultado del método en una variable nueva, simplemente lo perderás. -En lugar de modificar los valores, se pueden **reemplazar** los valores. Para ello, se necesitan variables: +Los otros métodos funcionan igual. ```python -name = 'Tirion' -name = name.upper() -print(name) # => TIRION +text = ' hi ' +cleaned = text.strip() +print(cleaned) # => 'hi', el resultado no contiene espacios +print(text) # => ' hi ', la cadena no cambió, contiene espacios ``` + +El método `strip()` devolvió una cadena nueva sin espacios, pero el propio `text` no cambió. + +Los métodos no modifican la cadena original porque las cadenas **no se pueden modificar después de crearse**. Esa decisión se tomó en Python a propósito. Modificar por accidente el valor de una cadena es imposible. Eso mismo simplifica el código **multihilo** y permite a Python reutilizar cadenas iguales en memoria, ahorrando recursos. + +Más adelante verás que la mutabilidad y la inmutabilidad son propiedades importantes, sobre todo al pasar datos a las funciones y al trabajar con colecciones. + +### Reasignación de la variable + +El resultado del método se puede escribir de vuelta en la misma variable. + +```python +text = ' hexlet ' +text = text.strip() +print(text) # => 'hexlet' +``` + +Eso es apropiado cuando la esencia de los datos no cambia. Después de `strip()` es el mismo texto, solo más limpio. Si el resultado del método representa otra entidad, vale la pena darle un nombre aparte. + +```python +full_name = 'John Doe' +header_name = full_name.upper() +``` + +`full_name` es el nombre de la persona. `header_name` es su variante para el encabezado. Son cosas distintas, y guardarlas en una misma variable sería un lío. El nombre de la variable debe reflejar el sentido de los datos que guarda. diff --git a/modules/38-objects/500-method-chain/es/EXERCISE.md b/modules/38-objects/500-method-chain/es/EXERCISE.md index 65ce9eab..5b79d8ea 100644 --- a/modules/38-objects/500-method-chain/es/EXERCISE.md +++ b/modules/38-objects/500-method-chain/es/EXERCISE.md @@ -1,2 +1,8 @@ +En la variable `text` se guarda una línea de log con espacios de más dentro. Toma la parte de la cadena **del carácter 5 al 15 inclusive**, elimina los espacios sobrantes de los extremos con el método `.strip()` y muestra en pantalla la **longitud de la cadena resultante**. -Usando el corte de cadenas, obtén una parte de la oración almacenada en la variable `text`, desde el carácter `5` hasta el carácter `15`, inclusive. Luego, procesa la subcadena obtenida con el método `.strip()` y muestra en pantalla la longitud de la subcadena resultante. Realiza estas operaciones en cadena sin crear variables intermedias. +Realiza todas las acciones **en una sola cadena de métodos**, sin crear variables intermedias. + +### Pistas + +* Los índices en las cadenas empiezan en cero. Por ejemplo, el 4.º carácter tiene el índice `3`. +* Al trabajar con cortes, el primer índice **se incluye** y el segundo **no**. Por ejemplo, `text[4:6]` devolverá el 5.º y el 6.º carácter de la cadena. diff --git a/modules/38-objects/500-method-chain/es/README.md b/modules/38-objects/500-method-chain/es/README.md index b8b98710..49cd5c1c 100644 --- a/modules/38-objects/500-method-chain/es/README.md +++ b/modules/38-objects/500-method-chain/es/README.md @@ -1,50 +1,98 @@ +Un método es una operación que se aplica a un valor y devuelve un resultado nuevo. Si el resultado permite de nuevo llamar a métodos, se le puede aplicar otro método más. Esa técnica se llama **cadena de métodos (method chaining)**. -En esta lección veremos cómo combinar diferentes enfoques al escribir código, así como también analizaremos los errores típicos de los principiantes. +```python +text = ' hExLeT ' +result = text.strip().lower() +print(result) # => 'hexlet' +``` + +1. El método `strip()` elimina los espacios al principio y al final de la cadena y devuelve `'hExLeT'`. +2. El método `lower()` pone todas las letras en minúscula y devuelve `'hexlet'`. -Tenemos el siguiente código: +![Cadena de métodos](./assets/methods-chain.png) + +Los métodos se llaman uno tras otro, como eslabones de una cadena. Eso permite escribir código compacto y legible. ```python -name = 'Tirion' -print(name.upper().lower()) # => `tirion` +print(' hExLeT '.strip().lower().replace('h', 'x')) # => xexlet + +# Lo mismo, pero sin cadena +text = ' hExLeT ' +step1 = text.strip() # 'hExLeT' +step2 = step1.lower() # 'hexlet' +step3 = step2.replace('h', 'x') # 'xexlet' +print(step3) ``` -Imprime en la pantalla `tirion`. +- `strip()` elimina los espacios. +- `lower()` pone las letras en minúscula. +- `replace('h', 'x')` cambia `h` por `x`. -Es la primera vez que vemos la sintaxis de varios puntos seguidos, pero todas las operaciones que aparecen aquí nos son familiares. En este código se combinan las capacidades conocidas hasta el momento del lenguaje estudiado. +Cada método devuelve una cadena nueva, y el método siguiente se aplica ya a esa cadena. -Esto ocurre a menudo en la programación. Si no conoces la sintaxis, aún puedes intentar combinar diferentes enfoques y existe la posibilidad de que funcionen. +```text +' Hello, World! '.strip().lower().replace('world', 'python') + │ │ │ + ↓ ↓ ↓ + 'Hello, World!' │ │ + 'hello, world!' │ + 'hello, python!' +``` -Para entender cómo funciona este código, debemos descomponer la cadena de llamadas en operaciones individuales: +## Orden de evaluación + +En una cadena de métodos el orden de ejecución va de izquierda a derecha. Cada método siguiente se llama sobre el resultado del anterior. ```python -name = 'Tirion' -upper_name = name.upper() # 'TIRION' -print(upper_name.lower()) # 'tirion' +print(' hExLeT '.strip().lower().replace('h', 'x')) # => xexlet ``` -Los primeros dos ejemplos son equivalentes. Podemos realizar operaciones secuencialmente con la creación intermedia de variables, o podemos construir una cadena continua de atributos y métodos. En las cadenas de llamadas, los cálculos siempre se realizan de izquierda a derecha. +1. `' hExLeT '` es la cadena de partida. +2. `.strip()` elimina los espacios y devuelve `'hExLeT'`. +3. `.lower()` pasa a minúsculas y devuelve `'hexlet'`. +4. `.replace('h', 'x')` reemplaza `'h'` por `'x'` y devuelve `'xexlet'`. -Otro ejemplo para reforzar el concepto: +Al usar funciones, la parte interna se ejecuta primero y su resultado se pasa a la función siguiente. ```python -name = 'Tirion' -print(name.replace('Ti', 'Ki').lower()) # => ? + # Ejemplo hipotético, si strip y lower fueran funciones +print(lower(strip(' hExLeT '))) ``` -Necesitamos pensar bien en este código. `.lower()` se aplica al resultado de la llamada al método que está a la izquierda. Y el método `replace()` devuelve una cadena. Los principiantes a menudo cometen errores en las cadenas de llamadas y olvidan hacer la ejecución: +Con los métodos simplemente te "mueves" de izquierda a derecha, leyendo la cadena como una frase normal. Eso hace que trabajar con métodos resulte especialmente cómodo. + +Si se confunde el orden, el resultado puede diferir: ```python -name = 'Tirion' -# ¡Este código no funcionará correctamente! -print(name.upper.lower()) +print(' hExLeT '.replace('h', 'x').strip().lower()) # => xexlet ``` -También es posible construir cadenas infinitamente largas e inútiles que incluyen listas o slices: +En este caso `replace()` actuará sobre la cadena con espacios. El resultado final salió igual, pero eso es más bien una coincidencia. En otras situaciones el orden sí importa. + +## Cadena después de un corte + +Los métodos se pueden llamar también después de otras operaciones, por ejemplo después de un corte de la cadena: ```python -name = 'Tirion' -# ¿Cuál es el resultado de esta ejecución? -print(name[1:5].upper().find('I')) +text = ' Hello, Hexlet! ' +# Eliminamos los espacios, tomamos la subcadena y pasamos a minúsculas +print(text.strip()[7:].lower()) # => hexlet! ``` -Esto no funciona con las funciones, ya que generalmente se anidan unas dentro de otras: `f(f(f()))`. Esto dificulta mucho el análisis. Pero esto no significa que no se pueda hacer de manera elegante. En otros lenguajes, esto se logra a través de la composición de funciones o el operador de canalización. +Aquí se llama primero a `strip()`, que elimina los espacios. Después tomamos el corte de la cadena `[7:]`, empezando por el octavo carácter. Y ya después de eso se llama a `lower()`, para pasar el resultado a minúsculas. + +Esa forma de escribir se lee de izquierda a derecha y muestra todo el recorrido de transformación de los datos en una sola línea. + +## Dónde termina la cadena + +La cadena se puede continuar mientras el resultado siga siendo una cadena de texto (u otro tipo que tenga métodos). Si un método devuelve un número u otro tipo simple, ya no se pueden llamar métodos más adelante: + +```python +text = 'hexlet' +length = text.upper().count('E') +print(length) # => 2 +``` + +El método `count()` devuelve el número `2`, y ese número ya no tiene métodos de cadena, por eso la cadena de métodos termina ahí. + +Las cadenas de métodos son una forma cómoda de unir varias operaciones sobre un valor sin variables intermedias. diff --git a/modules/38-objects/500-method-chain/es/assets/methods-chain.png b/modules/38-objects/500-method-chain/es/assets/methods-chain.png new file mode 100644 index 00000000..b2b4c619 Binary files /dev/null and b/modules/38-objects/500-method-chain/es/assets/methods-chain.png differ diff --git a/modules/40-define-functions/100-define-function/es/README.md b/modules/40-define-functions/100-define-function/es/README.md index 8a69bb46..fe383208 100644 --- a/modules/40-define-functions/100-define-function/es/README.md +++ b/modules/40-define-functions/100-define-function/es/README.md @@ -1,56 +1,87 @@ +Hasta este momento usábamos solo funciones ya listas: `print()`, `len()`, `max()` y otras. Pero en Python se pueden crear funciones propias, y llegó la hora de aprender a hacerlo. -Es más fácil escribir y mantener programas utilizando funciones propias. Permiten combinar operaciones compuestas en una sola. Por lo tanto, en esta lección hablaremos sobre cómo crear funciones propias. +## Para qué definir funciones -Supongamos que queremos enviar correos electrónicos en un sitio web, esto es un proceso bastante complejo que implica interactuar con sistemas externos. Pero si definimos una función, toda la complejidad se ocultará detrás de una función simple: +Supongamos que tenemos varios fragmentos de código parecidos: ```python -# Ejemplo hipotético -# Lugar donde se encuentra la función -from emails import send +print('Hello, Hexlet!') +print('Hello, world!') +print('Hello, Python!') +``` -email = 'support@hexlet.io' -title = 'Ayuda' -body = 'Escribí una historia de éxito, ¿cómo puedo obtener un descuento?' +Para no repetir la misma plantilla, podemos encapsularla en forma de función propia, que recibe un parámetro de entrada e imprime en pantalla la línea necesaria: -# Una pequeña ejecución - mucha lógica interna -send(email, title, body) +```python +def say_hello(name): + print(f'Hello, {name}!') ``` -Esta ejecución realiza acciones de gran lógica interna: se conecta al servidor de correo, forma una solicitud correcta basada en el título y el cuerpo del mensaje, y luego lo envía todo, sin olvidar cerrar la conexión. +Ahora podemos llamarla con distintos argumentos: + +```python +say_hello('Hexlet') # => Hello, Hexlet! +say_hello('world') # => Hello, world! +say_hello('Python') # => Hello, Python! +``` -Creemos nuestra primera función. Su tarea es mostrar un saludo en la pantalla: +Al parecer la cantidad de código no disminuyó, pero apareció otra cosa. Si esa función se usa en distintos lugares, cuando haga falta cambiar el texto nos bastará con corregir solo la definición de la función. Y cuanto más compleja sea la tarea y más a menudo se use en distintos lugares, más importante es extraer la lógica a funciones propias. + +## Sintaxis de la definición + +```python +def nombre_de_la_función(parámetros): + cuerpo +``` ```text -Hello, Hexlet! +def greet(name): ← nombre de la función y parámetro + return 'Hello, ' + name ← cuerpo de la función +│ │ +palabra devolución +clave del valor ``` +La palabra clave `def` empieza la definición. En `nombre_de_la_función` se admite cualquier nombre, igual que en una variable; entre paréntesis se indica la lista de parámetros separados por comas. Después de los dos puntos se sitúa el cuerpo de la función con una sangría de 4 espacios, en el que se escribe código Python normal. + +En Python las sangrías tienen valor sintáctico. Muestran qué código pertenece al cuerpo de la función. Mira el ejemplo: + ```python -# Definición de la función -# La definición no llama ni ejecuta la función -# Solo decimos que ahora existe esta función -def show_greeting(): - # Dentro del cuerpo, hay una sangría de cuatro espacios - text = 'Hello, Hexlet!' - print(text) +def say_hi(): + print('Hi!') -# Llamada a la función -show_greeting() # => '¡Hola, Hexlet!' +print('El programa continúa…') ``` -A diferencia de los datos normales, las funciones realizan acciones. Por lo tanto, sus nombres deben indicarse con verbos: "construir algo", "dibujar algo", "abrir algo". +Aquí la función `say_hi()` está definida, pero **`print('El programa continúa…')`** no pertenece a la función, porque no tiene sangría. Se ejecutará de inmediato al arrancar el programa, independientemente de la llamada a `say_hi()`. -La descripción que se encuentra debajo del nombre de la función con sangría se llama **cuerpo de la función**. Dentro del cuerpo se puede describir cualquier código. Es como un pequeño programa independiente, un conjunto de instrucciones arbitrarias. +Para que `say_hi()` actúe, hay que llamarla explícitamente: -El cuerpo se ejecuta cuando se ejecuta la función. Cada llamada a la función ejecuta el cuerpo de forma independiente de otras llamadas. +```python +def say_hi(): + print('Hi!') + +say_hi() # => Hi! +print('El programa continúa…') +``` -El cuerpo de la función puede estar vacío, en ese caso se utiliza la palabra clave `pass`: +## Ejemplo: función para imprimir la media aritmética + +Ahora implementaremos una función simple que **calcula e imprime la media aritmética** de dos números. La media aritmética es la suma de los números dividida entre su cantidad. Por ejemplo, la media de 6 y 4 se calcula así: `(6 + 4) / 2 = 5`. ```python -# Definición mínima de una función -def noop(): - pass +def print_average(a, b): + total = a + b + average = total / 2 + print(average) -noop() +print_average(6, 4) # => 5.0 ``` -El concepto de "crear una función" tiene muchos sinónimos: "implementar", "definir" e incluso "declarar". Se encuentran con frecuencia en la práctica. Al crear su propia función, facilitará las operaciones complejas y hará que el desarrollo sea más sencillo. +Aquí `a` y `b` son los parámetros de entrada, `total` contiene su suma, `average` se obtiene dividiendo la suma entre 2 y `print()` muestra el resultado. + +Al llamar a `print_average(6, 4)`, en la pantalla se mostrará `5.0`. + +## Reutilización y legibilidad + +Las funciones ayudan a evitar la duplicación y hacen que los programas sean más comprensibles. El nombre de la función por sí solo dice qué hace. Eso es especialmente importante en proyectos grandes, donde el código lo leen otros programadores (o tú mismo un mes después). diff --git a/modules/40-define-functions/150-return/es/README.md b/modules/40-define-functions/150-return/es/README.md index 6e03a5dd..4e444b6b 100644 --- a/modules/40-define-functions/150-return/es/README.md +++ b/modules/40-define-functions/150-return/es/README.md @@ -1,105 +1,77 @@ +En esta lección aprenderemos a escribir funciones que **devuelven valores**. Esas funciones responden a una pregunta y entregan el resultado de su trabajo, como si dijeran: "Toma, aquí lo tienes, ya lo he calculado". -En esta lección vamos a profundizar en cómo trabajar con las funciones creadas para que sean útiles. +Por ejemplo, una función puede devolver una cadena con el texto procesado o un número calculado con una fórmula. El valor devuelto se puede usar más adelante. Se guarda en una variable, se pasa a otra función o se muestra en pantalla. -Cuando definimos una función, esta imprime algunos datos en la pantalla: +![Sum-python](./assets/sum-python.jpg) -```python -def greeting(): - print('Hello, Hexlet!') -``` - -Estas funciones tienen poca utilidad, ya que no se puede utilizar su resultado dentro del programa. Veámoslo en un ejemplo. - -Tomemos el caso de procesar un correo electrónico. Cuando un usuario se registra en un sitio web, puede ingresar su correo electrónico de diferentes maneras: +Para que la función entregue el resultado, en ella se usa la palabra clave especial `return`. Termina la ejecución de la función e indica qué es exactamente lo que hay que devolver. -* Agregar espacios en blanco al principio o al final: `_support@hexlet.io__` -* Usar letras en diferentes casos: `SUPPORT@hexlet.io` - -Si lo guardamos en la base de datos en ese formato, el usuario no podrá iniciar sesión en el sitio web. Para evitar esto, es necesario preparar el correo electrónico antes de guardarlo en la base de datos: convertirlo a minúsculas y eliminar los espacios en blanco alrededor del texto. Esta tarea se puede resolver en un par de líneas: +Este es un ejemplo de una función que pone el texto en mayúsculas: ```python -def save_email(): - # El correo electrónico viene del formulario - correo = ' SoPoRtE@hexlet.IO' - # Eliminamos los espacios en blanco - trimmed_email = email.strip() - prepared_email = trimmed_email.lower() - print(prepared_email) - # Aquí se realizaría el guardado en la base de datos +def shout(name): + return name.upper() ``` -Este código es posible gracias a que se devuelve un valor. Los métodos `strip()` y `lower()` no imprimen nada en la pantalla, sino que **devuelven** el resultado de su trabajo. Por eso podemos asignar ese resultado a variables. Si imprimieran en la pantalla, no podríamos asignar el resultado a una variable. Por ejemplo, no podemos hacer esto con la función `greeting()`: +Llamamos a `shout()`, le pasamos un nombre y obtenemos una cadena en mayúsculas. Esa cadena es el resultado de la función. ```python -message = greeting() -# en realidad, la función print() devuelve None -# None es un objeto especial que se utiliza para representar la ausencia de valor -print(message) # => None +result = shout('hexlet') +print(result) # => HEXLET + +result2 = shout('code-basics') +print(result2) # => CODE-BASICS ``` -Ahora vamos a modificar la función `saludo()` para que devuelva datos. Para ello, en lugar de imprimir en la pantalla, utilizaremos la instrucción `return`: +A diferencia de `print()`, `return` no imprime nada. Simplemente devuelve el valor. La decisión de qué hacer con él la toma el código que llama. -```python -def greeting(): - return 'Hello, Hexlet!' -``` +Al llamar a la función `shout('hexlet')` se ejecuta primero la expresión `name.upper()`. Devuelve la cadena `'HEXLET'`. Después `return` entrega ese valor hacia fuera, allí desde donde se llamó a la función. En nuestro caso, ese valor se guarda en la variable `result` y luego se muestra en pantalla con `print()`. -`return` es una instrucción que toma la expresión que se encuentra a su derecha y la devuelve al código que llamó al método. Aquí finaliza la ejecución de la función. +## Devolución de una expresión evaluada + +Las funciones no están obligadas a devolver simplemente un parámetro. Normalmente en `return` se indica una **expresión**, que primero se evalúa y luego su resultado se pasa hacia fuera. ```python -# Ahora podemos utilizar el resultado de la función -message = greeting() -print(message) # => Hello, Hexlet! -# Incluso podemos realizar acciones con el resultado -print(mesagge.upper()) # => HELLO, HEXLET! +def full_name(first, last): + return first.capitalize() + ' ' + last.capitalize() ``` -Cualquier código después de `return` no se ejecuta: +En este ejemplo armamos el nombre completo a partir del nombre y el apellido. Primero se llama a los métodos `capitalize()`, luego las cadenas se unen con `+`, y la cadena ya lista se devuelve. ```python -def greeting_with_code_after_return(): - return 'Hello, Hexlet!' - print('Nunca me ejecutaré') +name = full_name('Aria', 'Stark') +print(name) # => Aria Stark ``` -Incluso si una función devuelve datos, eso no limita la posibilidad de imprimir en la pantalla. Además de devolver datos, también podemos imprimir: - -```python -def greeting_with_return_and_printing(): - print('Apareceré en la consola') - return 'Hello, Hexlet!' +Aquí, en la línea `return first.capitalize() + ' ' + last.capitalize()`, se ejecutan primero las dos llamadas a los métodos, luego se añade el espacio y solo entonces el resultado se pasa como valor de retorno. +## Funciones de varias líneas -# Esto imprimirá el texto en la pantalla y devolverá un valor -message = greeting_with_return_and_printing() -``` +A veces en el cuerpo de la función hay que dar varios pasos antes de obtener el resultado. En esos casos se escriben varias líneas de código y al final se usa `return` para devolver el valor final. -No solo se puede devolver un valor específico. Dado que `return` funciona con expresiones, puede haber cualquier cosa a la derecha de él. En este caso, debemos seguir los principios de legibilidad del código: +Por ejemplo, escribamos una función que formatea un nombre: elimina los espacios de los extremos y convierte todas las letras a mayúsculas. ```python -def greeting(): - message = 'Hello, Hexlet!' - return message +def format_name(name): + clean = name.strip() + uppercased = clean.upper() + return uppercased ``` -Aquí no estamos devolviendo la variable en sí, sino el valor que se encuentra en esa variable. A continuación, un ejemplo con cálculos: +Primero quitamos los espacios con el método `strip()`, después pasamos a mayúsculas con `upper()` y devolvemos el valor final. ```python -def double_five(): - # o return 5 + 5 - result = 5 + 5 - return result +print(format_name(' hexlet ')) # => HEXLET ``` -No es suficiente con definir una función. También es importante que sea útil y que se pueda utilizar su resultado. Ahora, piensa qué devolverá la llamada a la función `run()` que se define a continuación. - -```python -# Definición -def run(): - return 5 - return 10 +### Código después de `return` +Cuando Python llega al operador `return`, la ejecución de la función se detiene. Todo lo que esté escrito después de él dentro de la función **no se ejecutará**: -# ¿Qué se imprimirá en la pantalla? -print(run()) +```python +def example(): + return 'listo' + print('este código nunca se ejecutará') ``` + +Por eso `return` se escribe siempre al final de la lógica. Sin embargo, esos finales dentro de una función pueden ser muchos. Lo veremos con más detalle cuando lleguemos a las expresiones condicionales. diff --git a/modules/40-define-functions/150-return/es/assets/sum-python.jpg b/modules/40-define-functions/150-return/es/assets/sum-python.jpg new file mode 100644 index 00000000..85e8b129 Binary files /dev/null and b/modules/40-define-functions/150-return/es/assets/sum-python.jpg differ diff --git a/modules/40-define-functions/200-default-parameters/es/README.md b/modules/40-define-functions/200-default-parameters/es/README.md index 2c5562f5..bf0a64fd 100644 --- a/modules/40-define-functions/200-default-parameters/es/README.md +++ b/modules/40-define-functions/200-default-parameters/es/README.md @@ -1,53 +1,53 @@ +Las funciones pueden recibir parámetros. A veces resulta cómodo fijar el valor directamente en la definición de la función, para no indicarlo en cada llamada. Ese valor se llama **valor por defecto**. -En programación, muchas funciones y métodos tienen parámetros que rara vez cambian. En estos casos, se les asignan valores predeterminados que se pueden cambiar según sea necesario. Esto reduce la cantidad de código repetitivo. Veamos cómo se ve esto en la práctica. +Si el argumento no se pasa, se usa ese valor. Si el argumento se indica, reemplaza el valor por defecto. -Veamos un ejemplo: +## Ejemplo: repetición de texto + +Hagamos una función que repite una cadena varias veces. Por defecto, que sea una vez, pero si se quiere se puede indicar otra cantidad. ```python -# Función de potencia -# El segundo parámetro tiene un valor predeterminado de dos -def pow(x, base=2): - return x ** base - -# Tres al cuadrado (dos es el valor predeterminado) -pow(3) # 3 * 3 = 9 -# Tres al cubo -pow(3, 3) # 3 * 3 * 3 = 27 +def repeat(text, times=1): + return text * times + +print(repeat("Hi")) # => Hi +print(repeat("Hi", 3)) # => HiHiHi ``` -El valor predeterminado se ve como una asignación normal en la definición. Solo se activa si no se pasa el parámetro. +En este ejemplo se usa la operación de multiplicar una cadena por un número. Funciona así: se toma la cadena de partida y se repite la cantidad indicada de veces. Por ejemplo, `"A" * 5` se convertirá en `"AAAAA"`. Es una posibilidad incorporada de Python que se aplica a menudo al generar plantillas, separadores y fragmentos de texto repetidos. -Imagina que no llevaste las piezas de repuesto para tu automóvil al taller. Entonces el mecánico te ofrecerá poner las que él tiene, por defecto. +```text +def repeat(text, times=2): ← times tiene un valor por defecto + ... -Incluso puede haber un valor predeterminado cuando solo hay un parámetro: +repeat('go') → times = 2 (por defecto) +repeat('go', 5) → times = 5 (indicado explícitamente) +``` -```python -def my_print(text='nothing'): - print(text) +Los parámetros opcionales se indican siempre al final de la lista de parámetros. Por eso en el ejemplo va primero el parámetro obligatorio `text` y solo después de él se sitúa el parámetro `times` con su valor por defecto. -my_print() # => "nothing" -my_print("Hexlet") # => "Hexlet" -``` +## Ejemplo: unión de palabras con un separador -Puede haber cualquier cantidad de parámetros con valores predeterminados: +Por defecto las palabras se unen con un espacio, pero se puede indicar otro carácter. ```python -def f(a=5, b=10, c=100): +def join_words(word1, word2, sep=" "): + return word1 + sep + word2 + +print(join_words("King", "Road")) # => King Road +print(join_words("Dragon", "stone", "-")) # => Dragon-stone ``` -Los valores predeterminados tienen una restricción. Deben ir al final de la lista de parámetros. Desde el punto de vista de la sintaxis, no es posible crear una función en la que haya un parámetro opcional seguido de uno obligatorio: +## Ejemplo: varios parámetros por defecto -```python -# Este código dará un error -def f(a=5, b=10, c=100, x): -# Y este también -def f(a=5, b=10, x, c=100): +Una función puede contener más de un parámetro con valores por defecto. Por ejemplo, hagamos una función que construye una cadena separadora. Por defecto el carácter es un guion y la longitud es 10. -# Este código funcionará -def f(x, a=5, b=10, c=100): +```python +def make_line(symbol="-", length=10): + return symbol * length -# Este también funcionará -def f(x, y, a=5, b=10, c=100): +print(make_line()) # => ---------- +print(make_line("*")) # => ********** +print(make_line("*", 5)) # => ***** +print(make_line("#", 3)) # => ### ``` - -Ahora sabes cómo trabajar con valores predeterminados de los parámetros. Pueden estar presentes en varios parámetros o en uno solo. Y recuerda que los valores predeterminados deben estar al final de la lista de parámetros. Este conocimiento te ayudará a reducir la cantidad de código repetitivo. diff --git a/modules/40-define-functions/250-named-arguments/es/EXERCISE.md b/modules/40-define-functions/250-named-arguments/es/EXERCISE.md index 32eed1b3..ca78fb2e 100644 --- a/modules/40-define-functions/250-named-arguments/es/EXERCISE.md +++ b/modules/40-define-functions/250-named-arguments/es/EXERCISE.md @@ -1,10 +1,22 @@ +El programa prepara el texto de las notificaciones: quita los caracteres innecesarios del principio y repite el mensaje la cantidad de veces necesaria. Implementa la función `trim_and_repeat()`, que recibe tres parámetros: -Crea la función trim_and_repeat() con tres parámetros: una cadena de texto, offset (el número de caracteres a eliminar desde la izquierda) y repetitions (la cantidad de veces que se debe repetir la cadena recortada). Esta función recorta la cadena y repite la parte recortada tantas veces como sea necesario para alcanzar el número total de repeticiones especificado. -El valor predeterminado para offset es 0, y el valor predeterminado para repetitions es 1. La función debe combinar todas las repeticiones en una sola cadena y devolverla como resultado. +* Una cadena +* `offset`: la cantidad de caracteres que hay que recortar de la cadena por la izquierda +* `repetitions`: cuántas veces hay que repetir la cadena antes de devolver la cadena resultante -```python -text = 'python' -print(trim_and_repeat(text, offset=3, repetitions=2)) # => honhon -print(trim_and_repeat(text, repetitions=3)) # => pythonpythonpython -print(trim_and_repeat(text)) # => python -``` +La cantidad de caracteres del corte por defecto es 0, y la cantidad de repeticiones por defecto es 1. + +La función debe devolver la cadena obtenida. + + ```python + text = 'python' + + trim_and_repeat(text, offset=3, repetitions=2) # honhon + trim_and_repeat(text, repetitions=3) # pythonpythonpython + trim_and_repeat(text) # python + ``` + +## Pistas + +* Esta función se puede implementar de varias maneras +* Desde el punto de vista del sistema de comprobación no importa de qué manera se implemente por dentro la función `trim_and_repeat()`. Lo principal es que cumpla la tarea planteada diff --git a/modules/40-define-functions/250-named-arguments/es/README.md b/modules/40-define-functions/250-named-arguments/es/README.md index 4060d2a8..c8913afa 100644 --- a/modules/40-define-functions/250-named-arguments/es/README.md +++ b/modules/40-define-functions/250-named-arguments/es/README.md @@ -1,51 +1,53 @@ +Al llamar a una función, los argumentos se pueden pasar de dos formas. En la **llamada posicional** los valores van estrictamente por orden. El primero cae en el primer parámetro y el segundo en el segundo. Los **argumentos con nombre** permiten indicar explícitamente en la llamada el nombre del parámetro. Eso resulta cómodo cuando la función tiene muchos parámetros y hay que redefinir solo algunos. Los demás se quedan con sus valores por defecto. -En esta lección, vamos a analizar qué parámetros existen, cómo se diferencian y en qué casos se aplican. +```python +def repeat(text, times=1): + return text * times -**Argumentos** son los datos que se pasan a una función cuando se la llama. Hay dos tipos de argumentos: +repeat("Hi", 3) # llamada posicional +repeat(text="Hi", times=3) # llamada con nombres +``` -El primer tipo son los **argumentos posicionales**. Se pasan en el mismo orden en el que se definen los parámetros de la función: +Las dos variantes hacen lo mismo. En el segundo caso escribimos explícitamente que "Hi" es el valor de text y que 3 es el valor de times. Desde el punto de vista de la definición de la función nada cambió. Los argumentos con nombre funcionan para cualquier función, y la función misma no sabe nada de ello. Recibe los valores tal como están descritos en la definición. -```python -# (text, length) -truncate('My Text', 3) -``` +```text +def truncate(text, length): + ... -El segundo tipo son los **argumentos nombrados**. Se pasan no sólo como valores, sino como pares "nombre=valor". Por lo tanto, se pueden pasar en cualquier orden: +Posicionales: truncate('hello', 3) + └──┬──┘ └┬┘ + text length -```python -# Los argumentos se pasan en un orden diferente -truncate(length=3, text='My Text') +Con nombre: truncate(length=3, text='hello') + └──┬───┘ └─────┬─────┘ + length text ``` -Si observamos cuidadosamente los dos ejemplos anteriores, podemos entender que son dos funciones idénticas. - -Ahora vamos a entender en qué casos se deben utilizar estos tipos de argumentos. +Los argumentos con nombre se pueden indicar en cualquier orden. Eso no cambia el resultado del trabajo de la función, porque los valores se enlazan precisamente por el nombre del parámetro. -La elección del tipo de parámetro depende de quién llama a la función. +```python +repeat(times=3, text="Hi") # => HiHiHi +``` -Hay dos razones para usar argumentos nombrados: +Los argumentos con nombre se pueden combinar con los posicionales; en ese caso los posicionales van primero. Esa llamada permite indicar solo los parámetros que hay que redefinir. -* Mejoran la legibilidad, ya que los nombres son visibles de inmediato. +```python +repeat("Hi", times=3) # posicionales + con nombre => HiHiHi +``` -* No es necesario especificar todos los parámetros intermedios que no necesitamos en este momento. +## Cuándo usar los argumentos con nombre -Esto último es útil cuando una función tiene muchos parámetros opcionales. Veamos un ejemplo: +Los argumentos con nombre son útiles cuando la función tiene varios parámetros y no hay que cambiarlos todos. En esos casos se pueden indicar solo los parámetros que importan en la llamada concreta y dejar los demás con sus valores por defecto. ```python -def print_params(a=1, b=2, c=3, d=4): - print(a, b, c, d) +def make_line(symbol="-", length=10): + return symbol * length -# Solo necesitamos pasar d, pero tenemos que pasar todos los demás -f(1, 2, 3, 8) +make_line() # todos los parámetros por defecto -# Los argumentos nombrados nos permiten pasar solo d -# Los valores predeterminados se utilizan para los demás argumentos -f(d=8) +make_line(length=5) # cambiamos solo la longitud +# Sin esto habría que escribirlo así +make_line("-", 5) ``` -Los argumentos nombrados se pueden pasar junto con los posicionales. En ese caso, los posicionales deben ir al principio: - -```python -# Pasamos solo a (posicional) y d (nombrado) -f(3, d=3) -``` +No hubo que indicar el carácter, incluso a pesar de que va antes que la longitud en la lista de parámetros. Los argumentos con nombre hacen las llamadas más claras. Al leer el código se ve de inmediato qué valor corresponde a qué parámetro. diff --git a/modules/40-define-functions/300-type-annotations/es/EXERCISE.md b/modules/40-define-functions/300-type-annotations/es/EXERCISE.md index 66dd265f..2401f66c 100644 --- a/modules/40-define-functions/300-type-annotations/es/EXERCISE.md +++ b/modules/40-define-functions/300-type-annotations/es/EXERCISE.md @@ -1,13 +1,18 @@ +La aplicación crea separadores de texto a partir de caracteres repetidos, por ejemplo `-------` o `=====`. Implementa la función `word_multiply()`. Debe recibir dos parámetros: -Implementa la función `word_multiply()`. Debe aceptar dos parámetros: +* Una cadena +* Un número que indica cuántas veces hay que repetir la cadena -* Una cadena de texto -* Un número que indica cuántas veces se debe repetir la cadena +Y devuelve la cadena repetida n veces. Si se pasa cero, se devuelve una cadena vacía. ```python text = 'python' print(word_multiply(text, 2)) # => pythonpython -print(word_multiply(text, 0)) # => +print(word_multiply(text, 0)) # => ``` Especifica las anotaciones de tipos al declarar la función. + +## Pista + +* No olvides que la anotación de tipo también hay que indicarla en el valor de retorno diff --git a/modules/40-define-functions/300-type-annotations/es/README.md b/modules/40-define-functions/300-type-annotations/es/README.md index 7072de71..f01c16c3 100644 --- a/modules/40-define-functions/300-type-annotations/es/README.md +++ b/modules/40-define-functions/300-type-annotations/es/README.md @@ -1,34 +1,86 @@ +En Python se pueden pasar a una función cualesquiera valores. A veces eso complica la comprensión del código: no siempre queda claro qué espera exactamente la función y qué devuelve. Para hacer el código más claro, en Python existen las **anotaciones de tipos**. Con su ayuda se puede indicar explícitamente qué valores recibe la función y qué resultado devuelve. Así resolvemos varias tareas a la vez: -Las **Anotaciones de tipos** son una forma de especificar los tipos de los parámetros y el valor de retorno de una función en Python. No es un requisito obligatorio del lenguaje, pero puede ayudar a los programadores en el desarrollo posterior, así como mejorar la legibilidad del código y aumentar su confiabilidad. +- Mejoramos el trabajo del editor de código: obtenemos sugerencias, un autocompletado mejor y cosas por el estilo. +- Ayudamos a los agentes de IA a ver más rápido la estructura y a tomar decisiones más correctas, minimizando errores accidentales. +- Aparece la posibilidad de comprobar la corrección del programa sin ejecutarlo, mediante la comprobación estática. Esa comprobación no garantiza que la lógica del programa esté escrita correctamente, pero al menos no habrá en él errores de tipos. -Veamos un ejemplo simple de una función sin anotaciones de tipos: +![Type Hints In Editor](./assets/editor-hints.png) + +## Cómo indicar los tipos de los parámetros + +La anotación de una función describe dos elementos. Los tipos de los parámetros se indican directamente en la definición de la función, después del nombre de cada parámetro y separados por dos puntos. El tipo del resultado devuelto se indica después de la lista de parámetros con la flecha `->`. + +Analicémoslo con el ejemplo de una función que calcula la suma de dos valores pasados: ```python -def concat(first, second): - return first + second +def add(a: int, b: int) -> int: + return a + b + +print(add(2, 3)) # => 5 +``` + +```text +def concat(a: str, b: str) -> str: + │ │ │ + │ │ └── tipo del valor devuelto + │ └── tipo del parámetro b + └── tipo del parámetro a ``` -Esta función concatena dos cadenas en una sola. A simple vista, es difícil entender qué está sucediendo en el código: qué tipos tienen los argumentos, por qué la función trabaja con cadenas en lugar de sumar, por ejemplo, dos números. +Ahora el editor de código sugerirá que la función `add` recibe dos números y devuelve un número. Si se intenta pasar una cadena, el editor lo resaltará como un problema y avisará. + +```python +add("2", 3) # Argument of type "str" is not assignable to parameter of type "int" +``` + +## Qué tipos se usan en las anotaciones + +En esta etapa basta con conocer las anotaciones de los tipos de datos simples, primitivos: -Si más adelante utilizamos esta función en el código, puede ser necesario verificar los tipos de los argumentos antes de pasarlos a la función, lo que aumenta la cantidad de código y dificulta su comprensión. +- `int` para los números enteros, `float` para los números de coma flotante +- `str` para las cadenas +- `bool` para los valores lógicos (True o False) -Ahora agreguemos anotaciones de tipos a la función: +```python +def describe(name: str, age: int, height: float) -> str: + return f"{name}, {age} años, altura {height}" + +print(describe("Anna", 25, 1.70)) +# => Anna, 25 años, altura 1.7 +``` + +Si la función no devuelve nada, como tipo de retorno se indica `None`. Por ejemplo, una función puede solo imprimir texto en pantalla: ```python -def concat(first: str, second: str) -> str: - return first + second +def print_greeting(name: str) -> None: + print(f"Hello, {name}!") + +print_greeting("Anna") +# => Hello, Anna! ``` -Aquí hemos especificado que los argumentos `first` y `second` deben ser de tipo cadena (`str`). El valor de retorno también será una cadena. Cuando usemos esta función en el código, será más fácil entender qué tipos de argumentos se pueden pasar y qué tipo de valor de retorno se espera. +## Ejemplo con parámetros por defecto -Las anotaciones de tipos también se pueden utilizar para definir los tipos de las variables dentro de una función. Por ejemplo: +Las anotaciones funcionan igual tanto para los parámetros obligatorios como para los que tienen un valor por defecto. Primero se indica el tipo y luego, con `=`, el valor estándar. ```python -def double(n: int) -> int: - result: int = n * 2 - return result +def greet(name: str, greeting: str = "Hello") -> str: + return f"{greeting}, {name}" + +print(greet("Anna")) # => Hello, Anna +print(greet("Kirill", "Hi")) # => Hi, Kirill ``` -En este ejemplo, hemos definido el tipo de la variable `result` como `int`, utilizando una anotación de tipo. +En este ejemplo `name` es un parámetro obligatorio y `greeting` tiene un valor por defecto. Las anotaciones muestran los tipos de los dos parámetros y del resultado devuelto. + +## Las anotaciones y la comprobación del código + +Aunque Python en sí no comprueba las anotaciones durante la ejecución del programa, existen herramientas aparte que saben hacerlo y normalmente vienen incorporadas directamente en el editor. Ese enfoque se llama **comprobación estática del código**, es decir, una comprobación realizada sin ejecutar el código. + +"Estática" significa que la comprobación ocurre antes incluso de arrancar el programa. La herramienta lee el código fuente y verifica si los valores pasados corresponden a los tipos indicados. Por ejemplo, si la función recibe una cadena y tú le pasas un número, en la comprobación estática eso se mostrará como un error. + +![Type Errors In Editor](./assets/editor-errors.png) + +Resulta especialmente cómodo cuando el editor resalta esos errores justo mientras se escribe el código. Eso permite ver el problema de inmediato y corregirlo, sin esperar a ejecutar el programa. Gracias a ello muchos errores inesperados se detectan por adelantado y en el código en funcionamiento son menos. -Las anotaciones de tipos no son una verificación estricta de tipos en Python. Su uso no garantiza que la función se llame con argumentos y un valor de retorno de los tipos especificados. Después de todo, Python sigue siendo un lenguaje de tipado dinámico. Las anotaciones de tipos no afectan la capacidad de pasar argumentos de diferentes tipos o devolver valores de otros tipos. Sin embargo, su uso facilita la lectura y comprensión del código, y ayuda a detectar errores. +Las anotaciones de tipos no son obligatorias. Las funciones se pueden escribir también sin ellas: Python funcionará igualmente. Pero cuando hay anotaciones, el código se vuelve más claro para las personas y más cómodo para los editores. Anotar las funciones en tu propio código se considera una buena práctica. diff --git a/modules/40-define-functions/300-type-annotations/es/assets/editor-errors.png b/modules/40-define-functions/300-type-annotations/es/assets/editor-errors.png new file mode 100644 index 00000000..0983a5f5 Binary files /dev/null and b/modules/40-define-functions/300-type-annotations/es/assets/editor-errors.png differ diff --git a/modules/40-define-functions/300-type-annotations/es/assets/editor-hints.png b/modules/40-define-functions/300-type-annotations/es/assets/editor-hints.png new file mode 100644 index 00000000..0b96d978 Binary files /dev/null and b/modules/40-define-functions/300-type-annotations/es/assets/editor-hints.png differ diff --git a/modules/40-define-functions/350-modules/es/README.md b/modules/40-define-functions/350-modules/es/README.md index 768c9948..3a9f890a 100644 --- a/modules/40-define-functions/350-modules/es/README.md +++ b/modules/40-define-functions/350-modules/es/README.md @@ -1,10 +1,12 @@ -Un archivo Python con funciones y constantes se llama **módulo**. Se puede conectar a un programa usando la palabra clave `import` y usar código listo en lugar de escribirlo uno mismo. +Mientras el programa es pequeño, todo el código se puede guardar en un solo archivo. Ese enfoque resulta cómodo para ejemplos simples y tareas pequeñas. Pero con el tiempo el programa empieza a crecer. Cuando hay mucho código, orientarse en un solo archivo se vuelve difícil. Y las aplicaciones reales constan de decenas de miles de líneas (como mínimo) y de cientos de archivos. -Python incluye una biblioteca estándar con módulos que vienen con el lenguaje. Uno de los más utilizados es `math`, que proporciona funciones y constantes matemáticas. +Para dividir el programa en partes lógicas separadas, en Python se usan los módulos. Un módulo es un archivo con código y la extensión `.py`. Y el lenguaje ofrece mecanismos para acceder a las funciones y las constantes de otros archivos. -## Cómo importar un módulo +Además, Python mismo se distribuye ya con un conjunto de módulos incorporados (built-in) que se pueden usar en el propio código. Uno de los módulos que se usan a menudo es `math`. En él se encuentran funciones y constantes matemáticas. -El comando `import` al inicio del archivo hace que el contenido del módulo esté disponible: +## Cómo conectar un módulo + +La instrucción `import` al principio del archivo hace accesible el contenido del módulo: ```python import math @@ -13,25 +15,27 @@ print(math.floor(3.7)) # => 3 print(math.ceil(3.2)) # => 4 ``` -Después de importar, se accede a las funciones a través del nombre del módulo y un punto. `math.ceil(3.2)` llama a la función `ceil` del módulo `math`. +Después de la importación, a las funciones se accede a través del nombre del módulo y un punto. El nombre del módulo coincide con el nombre del archivo sin la extensión. `math.ceil(3.2)` llama a la función `ceil` del módulo `math`. + +Desde el punto de vista técnico, un módulo en Python es un objeto. Por eso el acceso mediante el punto funciona igual que al acceder a los métodos o las propiedades de los objetos. Al mismo tiempo, el módulo sirve ante todo para organizar el código. Normalmente, dentro de un módulo se sitúan funciones, constantes y datos auxiliares relacionados por una tarea común. ## Funciones del módulo math -`floor` redondea un número hacia abajo al entero más cercano, `ceil` lo redondea hacia arriba: +Analicemos algunas funciones de ese módulo. `floor()` redondea el número hacia abajo hasta el entero más cercano y `ceil()` redondea hacia arriba: ```python import math -print(math.floor(7.9)) # => 7 -print(math.ceil(7.1)) # => 8 -print(math.ceil(7.0)) # => 7 +print(math.floor(7.9)) # => 7 +print(math.ceil(7.1)) # => 8 +print(math.ceil(7.0)) # => 7 ``` -La diferencia importa cuando el número no es entero. `floor(7.9)` da 7, no 8, porque 7 es el entero más cercano por debajo. +La diferencia se nota cuando el número no es entero. `floor(7.9)` da 7 y no 8, porque 7 es el entero más cercano por debajo. -## Importar nombres específicos +## Importación de nombres concretos -Cuando solo se necesita una parte del módulo, se pueden importar nombres específicos: +Cuando solo hace falta una parte del módulo, se pueden importar nombres concretos: ```python from math import ceil, floor @@ -40,19 +44,47 @@ print(ceil(3.2)) # => 4 print(floor(3.7)) # => 3 ``` -Esto permite usar `ceil` y `floor` directamente, sin el prefijo `math.`. El resultado es el mismo que con `import math`. +Esa importación permite acceder a `ceil` y `floor` directamente, sin el prefijo `math.`. El resultado es el mismo que con `import math`. Esa forma es cómoda cuando del módulo solo hacen falta unas pocas funciones. Pero si el módulo es grande o los nombres de las funciones son demasiado generales, normalmente se prefiere el `import` normal, para que quede claro de dónde salió exactamente la función. -## Usar un módulo dentro de una función +## Un módulo dentro de una función -Un módulo importado está disponible en todo el archivo, incluido el cuerpo de las funciones: +El módulo importado está disponible en todo el archivo, incluido el cuerpo de las funciones: ```python import math +# Calcula la cantidad de viajes necesarios para transportar todos los objetos def trips_needed(items: int, capacity: int) -> int: return math.ceil(items / capacity) -print(trips_needed(10, 3)) # => 4 +print(trips_needed(10, 3)) # => 4 +``` + +## Conflictos de nombres + +Los módulos ayudan a evitar los conflictos de nombres. En programas grandes, distintas partes del código pueden contener funciones o variables con los mismos nombres. Por ejemplo, en el programa puede existir ya una función `floor`, y el módulo `math` también contiene una función con ese nombre. Gracias a la división en módulos podemos crear tantas funciones con el mismo nombre como queramos; lo importante es que estén en archivos distintos. Y eso funciona incluso con las importaciones: + +```python +import math + +def floor(number): + return "custom floor" + +print(floor(3.7)) # => custom floor +print(math.floor(3.7)) # => 3 +``` + +Gracias al nombre del módulo, Python entiende a qué función hay que llamar exactamente. Eso es especialmente importante al usar bibliotecas de terceros, porque distintos desarrolladores pueden elegir los mismos nombres para sus funciones. + +Los problemas surgen más a menudo al importar nombres concretos. Por ejemplo, si en el archivo ya existe una función con el mismo nombre: + +```python +from math import floor + +def floor(number): + return "custom floor" + +print(floor(3.7)) # => custom floor ``` -La función `trips_needed` usa `math.ceil`. La línea `import math` al inicio del archivo hace esto posible. +Después de definir la nueva función, el nombre `floor` empieza a referirse ya a ella. La función de `math` deja de estar accesible con ese nombre. Por esa razón, en muchos proyectos se prefiere el `import` normal, sobre todo en programas grandes y al trabajar con bibliotecas de terceros. diff --git a/modules/40-define-functions/400-packages/es/README.md b/modules/40-define-functions/400-packages/es/README.md index e5af893f..d3aed749 100644 --- a/modules/40-define-functions/400-packages/es/README.md +++ b/modules/40-define-functions/400-packages/es/README.md @@ -1,66 +1,112 @@ -Los programas Python casi siempre usan código escrito por otros. Reinventar funciones matemáticas, manejo de fechas o peticiones de red no tiene sentido — todo esto ya está disponible. Python usa paquetes para compartir y reutilizar código. +A medida que el programa crece aumenta no solo la cantidad de líneas de código, sino también la cantidad de módulos. En proyectos pequeños, decenas de archivos todavía se pueden mantener en un solo directorio, pero en aplicaciones reales los archivos pueden ser cientos y miles. -Un paquete es un directorio de archivos Python organizados por tema. La biblioteca estándar de Python contiene decenas de estos paquetes, que cubren desde matemáticas hasta el acceso al sistema de archivos. +Para organizar los módulos en grupos lógicos más grandes, en Python se usan los paquetes. Un paquete es un directorio con archivos de Python. -## Importar un paquete +Por ejemplo, la estructura de un proyecto puede verse así: -Para usar código de un paquete, hay que importarlo con la palabra clave `import`. Veamos el paquete `math`: +```text +project/ +├── main.py +├── payments/ +│ ├── stripe.py +│ └── paypal.py +└── users/ + ├── auth.py + └── profile.py +``` -```python -import math +En este ejemplo, `payments` y `users` son paquetes, y `stripe.py`, `paypal.py`, `auth.py` y `profile.py` son módulos. -print(math.sqrt(16)) # => 4.0 -print(math.pi) # => 3.141592653589793 -``` +Los paquetes ayudan a agrupar el código por tareas y simplifican la navegación por el proyecto. En lugar de cientos de archivos en un solo directorio, el código se divide en áreas de responsabilidad separadas. -Después de `import math`, todo el contenido del paquete está disponible con notación de punto: `math.sqrt`, `math.floor`, `math.pi`. +## Importación desde un paquete -## Importar nombres específicos +A los módulos que están dentro de un paquete se accede a través del punto: + +```python +import payments.stripe +``` -Una sintaxis alternativa permite importar solo lo que se necesita: +Después de la importación se puede usar el contenido del módulo: ```python -from math import sqrt, pi +import payments.stripe -print(sqrt(25)) # => 5.0 -print(pi) # => 3.141592653589793 +payments.stripe.create_payment() ``` -Ahora `sqrt` y `pi` están disponibles directamente, sin el prefijo `math.`. La forma `import math` hace explícito el origen de la función, lo que ayuda al leer código desconocido. La forma `from math import` es cómoda cuando se necesitan pocas funciones concretas y escribir el prefijo cada vez resulta tedioso. +El punto muestra el camino dentro del paquete. Python pasa secuencialmente del paquete al módulo. -## Alias +## Importación de un módulo concreto -La palabra clave `as` permite dar un alias a un nombre importado: +A menudo no se importa todo el camino, sino un módulo concreto: ```python -from math import sqrt as sq +from payments import stripe -print(sq(9)) # => 3.0 +stripe.create_payment() ``` -Un alias es útil cuando un nombre entra en conflicto con una variable existente o es demasiado largo. +Esa forma acorta la escritura y hace el código más compacto. + +## Paquetes anidados -## Biblioteca estándar +Los paquetes pueden contener otros paquetes: -Python viene con un conjunto amplio de paquetes integrados. Algunos de uso frecuente: +```text +project/ +└── app/ + └── payments/ + ├── stripe/ + │ ├── client.py + │ └── api.py + └── paypal.py +``` -- `math` — funciones matemáticas: `sqrt`, `hypot`, `floor`, `ceil`, `pi` -- `random` — números aleatorios: `randint`, `choice`, `shuffle` -- `datetime` — trabajo con fechas y horas +En ese caso el camino hasta el módulo se vuelve más largo: ```python -import random +from app.payments.stripe import client +``` + +Esa estructura se encuentra a menudo en proyectos grandes. Ayuda a dividir el código en partes independientes. -print(random.randint(1, 6)) # => número aleatorio entre 1 y 6 -print(random.choice(['rock', 'paper', 'scissors'])) # => elemento aleatorio +## Paquetes y espacios de nombres + +Los paquetes crean un nivel adicional de espacio de nombres. Gracias a eso, distintas partes del programa pueden contener módulos con los mismos nombres. + +Por ejemplo: + +```text +project/ +├── admin/ +│ └── config.py +└── user/ + └── config.py +``` + +Aquí existen dos módulos `config.py` distintos. + +Python los distingue por el camino completo: + +```python +import admin.config +import user.config ``` -## Paquetes de terceros +Sin los paquetes, esos nombres entrarían en conflicto entre sí. -Más allá de la biblioteca estándar, existen miles de paquetes creados por desarrolladores de todo el mundo. Se instalan con `pip`, el gestor de paquetes de Python: +## El archivo __init__.py -```bash -pip install requests +Antes, para crear un paquete, dentro del directorio se colocaba obligatoriamente el archivo `__init__.py`: + +```text +payments/ +├── __init__.py +├── stripe.py +└── paypal.py ``` -Después de la instalación, las importaciones funcionan igual que con los paquetes integrados. +Ese archivo informaba a Python de que el directorio había que tratarlo como un paquete. + +En las versiones modernas de Python los paquetes pueden funcionar también sin `__init__.py`. No obstante, ese archivo se sigue usando a menudo. Normalmente en él se coloca código común y la configuración del paquete. diff --git a/modules/45-logic/10-bool-type/es/EXERCISE.md b/modules/45-logic/10-bool-type/es/EXERCISE.md index ded57273..c99665d5 100644 --- a/modules/45-logic/10-bool-type/es/EXERCISE.md +++ b/modules/45-logic/10-bool-type/es/EXERCISE.md @@ -1,5 +1,4 @@ - -Escriba una función `is_pensioner()` que tome la edad como único argumento y verifique si esa edad corresponde a la jubilación. Se considera jubilación a partir de los 60 años. +Escribe la función `is_pensioner()`, que recibe la edad como único argumento y comprueba si esa edad es de jubilación. Se considera edad de jubilación los 60 años o más. Ejemplos de llamadas: diff --git a/modules/45-logic/10-bool-type/es/README.md b/modules/45-logic/10-bool-type/es/README.md index 39dcc63a..4f6f0e6e 100644 --- a/modules/45-logic/10-bool-type/es/README.md +++ b/modules/45-logic/10-bool-type/es/README.md @@ -1,46 +1,59 @@ +Además de las operaciones aritméticas, en matemáticas existen las operaciones de comparación, por ejemplo `5 > 4` o `3 < 1`. También existen en programación. Las comparaciones se aplican a menudo en tareas reales relacionadas con números. Por ejemplo, cuando tramitamos una compra en una tienda en línea, el sistema comprueba si al usuario le alcanza el dinero de la cuenta para pagar el producto. Si el importe de la cuenta es mayor o igual que el precio del producto, el pedido se confirma. Si faltan fondos, aparece el mensaje correspondiente. -Además de las operaciones aritméticas en matemáticas, existen operaciones de comparación, como `5 > 4` o `3 < 1`. También existen en programación. Por ejemplo, cuando ingresamos a un sitio web, se comparan el nombre de usuario y la contraseña ingresados con los que están en la base de datos. Si coinciden, se nos permite ingresar, es decir, se autentica. En esta lección, aprenderemos sobre las operaciones de comparación. +## La comparación en programación -Los lenguajes de programación han adaptado todas las operaciones de comparación matemáticas sin cambios, excepto los operadores de igualdad y desigualdad. En matemáticas, se utiliza el signo de igual `=`, pero en programación esto rara vez se encuentra. +Empecemos con un ejemplo en el que se comparan dos números: -En muchos lenguajes, el símbolo `=` se utiliza para asignar valores a variables. Por lo tanto, en Python se compara con `==`. +```python +print(5 > 4) # => True +print(4 > 4) # => False +``` + +El resultado de una comparación es un valor del tipo `bool`. Ese tipo tiene solo dos variantes posibles: `True` y `False`. Son valores especiales del lenguaje. Se pueden usar directamente: + +```python +print(True) +print(False) +``` -Lista de operaciones de comparación: +En la práctica rara vez se usan así, pero sobre ellos se construye la lógica del comportamiento del programa. Nos encontramos con eso literalmente todos los días, cuando introducimos códigos PIN y contraseñas, cuando ejecutamos acciones cuyo resultado puede tener distintas variantes. Todas esas variantes están escritas dentro del programa en forma de expresiones condicionales. El programa razona más o menos así: *si es de esta manera haz una cosa, si es de otra manera haz la otra*. -* `<` — menor que -* `<=` — menor o igual que -* `>` — mayor que -* `>=` — mayor o igual que -* `==` — igual que -* `!=` — no igual que +En Python están disponibles las siguientes operaciones de comparación: -Estas operaciones no solo se aplican a números. Por ejemplo, con el operador de igualdad se pueden comparar cadenas de texto: `password == text` es una comparación de identidad entre cadenas de texto que están almacenadas en diferentes variables. +* `<` y `<=` significan "menor" y "menor o igual" +* `>` y `>=` significan "mayor" y "mayor o igual" +* `==` y `!=` significan "igual" y "distinto" -Una operación lógica como `5 > 4` o `password == text` es una expresión. Su resultado es un valor especial, `True` (verdadero) o `False` (falso). Este es un nuevo tipo de dato para nosotros, el tipo `bool`. +Los lenguajes de programación adaptaron todas las operaciones matemáticas de comparación sin cambios, excepto los operadores de igualdad y desigualdad. En matemáticas para eso se usa el igual normal `=`, pero en programación el símbolo `=` se usa, la mayoría de las veces, para asignar valores a las variables. Por eso en Python se compara con `==`. Algunos ejemplos: ```python -result = 5 > 4 -print(result) # => True -print('one' != 'one') # => False +print(5 >= 3) # => True +print(7 < 0) # => False +print(5 > 5) # => False +print(5 >= 5) # => True +print(2 == 5) # => False +print(2 != 5) # => True ``` -Junto con las cadenas de texto (`str`) y los números enteros y racionales, el **tipo `bool` (booleano)** es uno de los tipos de datos primitivos en Python. - -Intentemos escribir una función simple que tome la edad de un niño y determine si es un bebé. Se considera bebé a los niños menores de un año: +Cuando en una comparación se usan valores concretos, esa operación parece carente de sentido: ya conocemos su resultado y siempre es el mismo, porque si 3 es mayor que 2, eso no cambia. Pero todo cambia cuando los valores pueden variar. Intentemos escribir una función simple que recibe la edad de un niño y determina si es un bebé. Se consideran bebés los niños de menos de dos años (el dos no se incluye). ```python -def is_infant(age): - return age < 1 +def is_infant(age: int) -> bool: + return age < 2 print(is_infant(3)) # => False +print(is_infant(2)) # => False +print(is_infant(1)) # => True +print(is_infant(0)) # => True ``` -Cualquier operación es una expresión, por lo que en una sola línea de la función escribimos "devolver el valor que resulte de la comparación `age < 1`". Dependiendo del argumento que se pase, la comparación será verdadera (`True`) o falsa (`False`), y `return` devolverá ese resultado. - -Ahora probemos con un niño de seis meses: +Cuando las funciones devuelven el resultado de una comparación, normalmente responden a la pregunta con "sí" o "no". Esas funciones se llaman **predicados**. Se reconocen con facilidad porque devuelven un valor lógico `True` o `False`. A menudo en su nombre hay una pregunta o una afirmación que se puede comprobar ('is', 'has', 'can'). Esta es una función que comprueba si un número es negativo: ```python -print(is_infant(0.5)) # => True -``` +def is_negative(number: int) -> bool: + # Comprobamos si el número es menor que cero + return number < 0 -El resultado de la operación es `True`. Por lo tanto, el niño es realmente un bebé. +print(is_negative(-5)) # => True +print(is_negative(7)) # => False +``` diff --git a/modules/45-logic/15-bool-strings/es/README.md b/modules/45-logic/15-bool-strings/es/README.md new file mode 100644 index 00000000..fb0de19e --- /dev/null +++ b/modules/45-logic/15-bool-strings/es/README.md @@ -0,0 +1,64 @@ +Las operaciones de comparación funcionan con números y con cadenas. En Python las cadenas se comparan lexicográficamente: carácter por carácter, de izquierda a derecha, por los códigos numéricos de los caracteres (Unicode). + +```python +print("apple" < "banana") # => True +print("cat" > "dog") # => False +print("abc" == "abc") # => True +print("hello" != "world") # => True +``` + +Aquí `"apple" < "banana"` porque el código de `a` (97) es menor que el código de `b` (98), y es precisamente el primer carácter el que decide el resultado de la comparación. El código de un carácter se puede ver así: `ord('a')` → `97`. + +```python +print(ord("a")) # => 97 +print(ord("b")) # => 98 +``` + +La comparación distingue mayúsculas y minúsculas: `'Z'` (90) < `'a'` (97). Un ejemplo de comparación en el que las primeras letras son de distinto caso: + +```python +print("Zebra" < "apple") # True — 'Z'(90) < 'a'(97) +print("apple" < "Banana") # False — 'a'(97) > 'B'(66) +print("Apple" < "apple") # True — 'A'(65) < 'a'(97) +``` + +Escribamos una función que comprueba si una palabra empieza por una letra dada. Para eso tomamos el primer carácter de la cadena y lo comparamos con la letra necesaria. + +```python +def starts_with(word: str, letter: str) -> bool: + return word[0] == letter + +print(starts_with("apple", "a")) # => True +print(starts_with("banana", "a")) # => False +``` + +Las operaciones de comparación son expresiones igual que las aritméticas. En ellas se pueden sustituir valores ya listos y otras expresiones, como en el ejemplo de arriba: `word[0]`. Por ejemplo, en lugar de un número se puede usar el resultado del trabajo de la función `len`, que devuelve la longitud de una cadena: + +```python +print(len("apple") > 3) # => True, porque len("apple") = 5 +print(len("hi") > 3) # => False, porque len("hi") = 2 +``` + +En el ejemplo de arriba se ejecuta primero la función `len("apple")`, cuyo resultado será el número `5`. Después ese número se compara con `3`. En otras palabras, primero se calculan los argumentos de la expresión (por ejemplo, la longitud de la cadena) y luego se ejecuta la operación de comparación. + +Así se pueden combinar distintas operaciones, obteniendo comprobaciones más complejas. + +## Predicados útiles + +Las cadenas en Python tienen muchos métodos-predicado incorporados. Devuelven `True` o `False` y ayudan a comprobar distintas propiedades de la cadena. Abajo están los que se usan con más frecuencia: + +```python +print("hello".startswith("he")) # True — la cadena empieza por "he" +print("hello".endswith("lo")) # True — la cadena termina en "lo" + +print("123".isdigit()) # True — todos los caracteres son dígitos +print("abc".isalpha()) # True — todos los caracteres son letras +print("abc123".isalnum()) # True — la cadena consta solo de letras y dígitos + +print(" ".isspace()) # True — la cadena contiene solo espacios +print("Hello".islower()) # False — no todos los caracteres están en minúscula +print("HELLO".isupper()) # True — todos los caracteres están en mayúscula +print("Title Case".istitle()) # True — cada palabra empieza por mayúscula +``` + +Esos métodos permiten comprobar las cadenas según las condiciones necesarias directamente en el código, sin escribir funciones adicionales. diff --git a/modules/45-logic/20-logic-combine-expressions/es/EXERCISE.md b/modules/45-logic/20-logic-combine-expressions/es/EXERCISE.md index f24ce59c..1bc4f48b 100644 --- a/modules/45-logic/20-logic-combine-expressions/es/EXERCISE.md +++ b/modules/45-logic/20-logic-combine-expressions/es/EXERCISE.md @@ -1,5 +1,4 @@ - -Implementa la función `is_international_phone()`, que verifica el formato de un número de teléfono especificado. Si el teléfono comienza con un `+`, significa que está en formato internacional. +Implementa la función `is_international_phone()`, que recibe de entrada una cadena —un número de teléfono— y comprueba su formato. Si el teléfono empieza por *+*, significa que está en formato internacional. ```python is_international_phone('89602223423') # False diff --git a/modules/45-logic/20-logic-combine-expressions/es/README.md b/modules/45-logic/20-logic-combine-expressions/es/README.md index c67de6a7..468cafa3 100644 --- a/modules/45-logic/20-logic-combine-expressions/es/README.md +++ b/modules/45-logic/20-logic-combine-expressions/es/README.md @@ -1,52 +1,50 @@ +Las **operaciones lógicas** son expresiones, por eso se pueden combinar con otras expresiones. Por ejemplo, queremos comprobar la paridad de un número, es decir, si es múltiplo de dos. En programación, para eso se comprueba el resto de la división entre dos. Si el resto es igual a `0`, el número es par. Si el resto no es igual a `0`, el número es impar. -**Las operaciones lógicas** son expresiones, por lo tanto, se pueden combinar con otras expresiones. Por ejemplo, queremos verificar si un número es par, es decir, si es divisible por dos. En programación, se utiliza el siguiente enfoque: se verifica el residuo de la división por dos: +![even-or-odd](./assets/even-or-odd.jpg) -* si el residuo es `0`, el número es par -* si el residuo no es `0`, el número es impar +El resto de la división es un concepto simple pero importante en aritmética, álgebra, teoría de números y criptografía. Hay que dividir un número en varios grupos iguales, y si al final queda algo, eso es precisamente el resto de la división. -El residuo de la división es un concepto simple pero importante en aritmética, álgebra, teoría de números y criptografía. Consiste en dividir un número en grupos iguales y si queda algo al final, ese es el residuo de la división. +Repartimos caramelos a partes iguales entre personas: -Por ejemplo, repartimos caramelos equitativamente entre personas: +* 7 caramelos, 2 personas: 2 x 3 + resto 1 (7 no es múltiplo de 2) +* 21 caramelos, 3 personas: 3 x 7 + resto 0 (21 es múltiplo de 3) +* 19 caramelos, 5 personas: 5 x 3 + resto 4 (19 no es múltiplo de 5) -* 7 caramelos, 2 personas: 2 x 3 + residuo 1 — 7 no es divisible por 2 -* 21 caramelos, 3 personas: 3 x 7 + residuo 0 — 21 es divisible por 3 -* 19 caramelos, 5 personas: 5 x 3 + residuo 4 — 19 no es divisible por 5 - -El operador `%` calcula el residuo de la división: +El operador `%` calcula el resto de la división: * `7 % 2` → `1` * `21 % 3` → `0` * `19 % 5` → `4` -Vamos a combinar el operador lógico "igualdad" `==` y el operador aritmético `%` en una sola expresión y escribiremos una función para verificar si un número es par: +Combinemos en una misma expresión el operador lógico de "comprobación de igualdad" `==` y el operador aritmético `%`, y escribamos una función de comprobación de la paridad: ```python -def is_even(number): +def is_even(number: int) -> bool: return number % 2 == 0 print(is_even(10)) # => True print(is_even(3)) # => False ``` -Las operaciones aritméticas tienen mayor prioridad que las operaciones lógicas. Esto significa que primero se calcula la expresión aritmética `numero % 2` y luego se compara el resultado con cero para verificar la igualdad. +La prioridad de las operaciones aritméticas es mayor que la de las lógicas. Eso significa que primero se evalúa la expresión aritmética `number % 2` y luego el resultado se compara con cero y se devuelve el resultado de la comprobación de igualdad. -Ahora vamos a escribir una función que recibe una cadena de texto y verifica si esa cadena comienza con la letra `a` en minúscula. +Ahora escribamos una función que recibe una cadena y comprueba si esa cadena empieza por la letra latina `a`. -Algoritmo: +El algoritmo: -1. Obtener y guardar el primer carácter de la cadena de texto argumento -2. Comparar si el carácter es igual a la letra `a` -3. Devolver el resultado +1. Obtenemos y guardamos en una variable el primer carácter de la cadena-argumento +2. Comparamos si el carácter es igual a la letra latina `a` +3. Devolvemos el resultado ```python -def is_first_letter_an_a(string): - first_letter = string[0] +def is_first_letter_an_a(text: str) -> bool: + first_letter = text[0] return first_letter == 'a' print(is_first_letter_an_a('orange')) # => False print(is_first_letter_an_a('apple')) # => True ``` -Para comprender lo que está sucediendo aquí, intente explicar el proceso de manera similar a cómo desglosamos el ejemplo de `is_even()`. +Para que quede claro qué ocurre aquí, intenta decir en voz alta lo que pasa, igual que descifrábamos el proceso en el ejemplo con `is_even()`. -Ahora sabes que las operaciones de comparación se utilizan en programación junto con las operaciones aritméticas. Pero recuerda que la igualdad se representa con `==`. De esta manera, no confundirás esta operación con la asignación de un valor a una variable. +Ahora sabes que las operaciones de comparación se aplican en programación al mismo nivel que las aritméticas. Pero recuerda que la igualdad se denota con `==`. Así no confundirás esa operación con la asignación de un valor a una variable. diff --git a/modules/45-logic/20-logic-combine-expressions/es/assets/even-or-odd.jpg b/modules/45-logic/20-logic-combine-expressions/es/assets/even-or-odd.jpg new file mode 100644 index 00000000..6c3197c8 Binary files /dev/null and b/modules/45-logic/20-logic-combine-expressions/es/assets/even-or-odd.jpg differ diff --git a/modules/45-logic/25-logical-operators/es/EXERCISE.md b/modules/45-logic/25-logical-operators/es/EXERCISE.md index b626272e..c4258b21 100644 --- a/modules/45-logic/25-logical-operators/es/EXERCISE.md +++ b/modules/45-logic/25-logical-operators/es/EXERCISE.md @@ -1,5 +1,4 @@ - -Implementa el método `is_leap_year()`, que determina si un año es bisiesto o no. Un año es bisiesto si es divisible (es decir, no tiene residuo) por 400 o si es divisible por 4 y no es divisible por 100 al mismo tiempo. Como puedes ver, toda la lógica necesaria ya está incluida en la definición, solamente falta trasladarla al código: +Implementa la función `is_leap_year()`, que recibe un año en forma de número y determina si es bisiesto o no. Un año será bisiesto si es múltiplo de (es decir, se divide sin resto entre) 400, o si a la vez es múltiplo de 4 y no es múltiplo de 100. Como ves, en la definición ya está contenida toda la lógica necesaria; solo queda trasladarla al código: ```python is_leap_year(2018) # false @@ -7,13 +6,13 @@ is_leap_year(2017) # false is_leap_year(2016) # true ``` -Puedes verificar la divisibilidad de la siguiente manera: +La multiplicidad se puede comprobar así: ```python -# % - devuelve el residuo de la división del operando izquierdo por el operando derecho -# Verifica si number es divisible por 10 +# % - devuelve el resto de la división del operando izquierdo entre el derecho +# Comprobamos que number es múltiplo de 10 number % 10 == 0 -# Verifica si number no es divisible por 10 +# Comprobamos que number no es múltiplo de 10 number % 10 != 0 ``` diff --git a/modules/45-logic/25-logical-operators/es/README.md b/modules/45-logic/25-logical-operators/es/README.md index bf30e641..886adbd5 100644 --- a/modules/45-logic/25-logical-operators/es/README.md +++ b/modules/45-logic/25-logical-operators/es/README.md @@ -1,17 +1,16 @@ +Ya sabemos escribir funciones que comprueban condiciones sueltas. Y en esta lección aprenderemos a construir condiciones compuestas. -Ya sabemos cómo escribir funciones que verifican condiciones individuales. En esta lección aprenderemos a construir condiciones compuestas. - -Supongamos que un sitio web requiere que la contraseña tenga más de ocho caracteres y al menos una letra mayúscula al registrarse. Intentemos escribir dos expresiones lógicas separadas y combinarlas con el operador especial "Y": +Supongamos que un sitio exige, al registrarse, que la contraseña tenga más de ocho caracteres y contenga al menos una letra mayúscula. Intentemos escribir dos expresiones lógicas separadas y unirlas con el operador especial "Y": > La contraseña tiene más de 8 caracteres **Y** la contraseña contiene al menos una letra mayúscula -Aquí hay una función que toma una contraseña y devuelve si cumple con las condiciones (`True`) o no (`False`): +Esta es una función que recibe una contraseña y dice si cumple las condiciones (`True`) o no las cumple (`False`): ```python -def has_capital_letter(string): - # Verifica si hay al menos una letra mayúscula en la cadena +def has_capital_letter(text: str) -> bool: + # Comprueba la presencia de al menos una letra mayúscula en la cadena -def is_correct_password(password): +def is_correct_password(password: str) -> bool: length = len(password) return length > 8 and has_capital_letter(password) @@ -20,31 +19,40 @@ print(is_correct_password('Qwerty1234')) # => True print(is_correct_password('qwerty1234')) # => False ``` -`and` significa "Y". En lógica matemática, esto se llama conjunción. La expresión completa se considera verdadera si cada **operando** es verdadero, es decir, cada una de las expresiones compuestas. En otras palabras, `and` significa "y esto, y aquello". La prioridad de este operador es menor que la de los operadores de comparación. Por lo tanto, la expresión `has_capital_letter(password) and length > 8` también funciona correctamente sin paréntesis. - -Además de `and`, se utiliza frecuentemente el operador `or` - "O" (disyunción). Significa "o esto, o aquello, o ambos". La expresión `a or b` se considera verdadera si al menos uno de los operandos o todos ellos son verdaderos. En caso contrario, la expresión es falsa. +El operador `and` significa "Y". En lógica matemática eso se llama conjunción. Toda la expresión se considera verdadera si es verdadero cada **operando**, es decir, cada una de las expresiones que la componen. En otras palabras, `and` significa "tanto una cosa como la otra". La prioridad de ese operador es menor que la de los operadores de comparación. Por eso la expresión `has_capital_letter(password) and length > 8` también funciona correctamente sin paréntesis. -Los operadores se pueden combinar en cualquier cantidad y en cualquier secuencia. Si en el código se encuentran `and` y `or` al mismo tiempo, se establece la prioridad mediante paréntesis. A continuación se muestra un ejemplo de una función extendida que determina la corrección de una contraseña: +Además de `and` se usa a menudo el operador `or`, que significa "O" (disyunción). Significa "o una cosa, o la otra, o las dos". La expresión `a or b` se considera verdadera si al menos uno de los operandos es verdadero o cuando los dos operandos son verdaderos. En el otro caso la expresión es falsa. ```python -def has_capital_letter(string): - # Verifica si hay al menos una letra mayúscula en la cadena +def has_discount(age: int, is_student: bool) -> bool: + return age < 18 or is_student + +print(has_discount(15, False)) # => True (menor de 18) +print(has_discount(25, True)) # => True (estudiante) +print(has_discount(15, True)) # => True (las dos condiciones) +print(has_discount(25, False)) # => False +``` + +Los operadores se pueden combinar en cualquier cantidad y en cualquier secuencia. Si en el código aparecen a la vez `and` y `or`, es mejor fijar la prioridad con paréntesis. Abajo hay un ejemplo de una función ampliada que determina la corrección de una contraseña: -def has_special_chars(string): - # Verifica si hay caracteres especiales en la cadena +```python +def has_capital_letter(text: str) -> bool: + # Comprueba la presencia de al menos una letra mayúscula en la cadena +def has_special_chars(text: str) -> bool: + # Comprueba la presencia de caracteres especiales en la cadena -def is_strong_password(password): +def is_strong_password(password) -> bool: length = len(password) - # Los paréntesis establecen la prioridad. Es claro a qué se refiere cada uno. + # Los paréntesis fijan la prioridad. Queda claro qué se refiere a qué. return (length > 8 and has_capital_letter(password)) and has_special_chars(password) ``` -Ahora, imaginemos que queremos comprar un apartamento que cumpla con las siguientes condiciones: un área de al menos 100 metros cuadrados en cualquier calle **O** un área de al menos 80 metros cuadrados, pero en la calle principal `Main Street`. +Ahora imaginemos que queremos comprar un piso que cumpla estas condiciones: una superficie de 100 metros cuadrados o más en cualquier calle **O** una superficie de 80 metros cuadrados o más, pero en la calle central `Main Street`. -Escribamos una función que verifique el apartamento. Toma dos argumentos: el área - un número y el nombre de la calle - una cadena: +Escribamos una función que compruebe el piso. Recibe dos argumentos: la superficie (un número) y el nombre de la calle (una cadena): ```python -def is_good_apartment(area, street): +def is_good_apartment(area: int, street: str) -> bool: return area >= 100 or (area >= 80 and street == 'Main Street') print(is_good_apartment(91, 'Queens Street')) # => False @@ -56,7 +64,7 @@ print(is_good_apartment(120, 'Main Street')) # => True print(is_good_apartment(80, 'Main Street')) # => True ``` -El área de las matemáticas que estudia los operadores lógicos se llama álgebra booleana. A continuación, verás las **tablas de verdad** - con ellas puedes determinar el resultado al aplicar los operadores: +El área de las matemáticas en la que se estudian los operadores lógicos se llama álgebra booleana. Las **tablas de verdad** muestran cuál será el resultado al aplicar cada operador. #### Y `and` diff --git a/modules/45-logic/28-logical-negation/es/EXERCISE.md b/modules/45-logic/28-logical-negation/es/EXERCISE.md index 8da0dc06..2a31bf9e 100644 --- a/modules/45-logic/28-logical-negation/es/EXERCISE.md +++ b/modules/45-logic/28-logical-negation/es/EXERCISE.md @@ -1,21 +1,20 @@ +En un juego de palabras los participantes comprueban si una palabra es un palíndromo. Implementa dos funciones, `is_palindrome()` e `is_not_palindrome()`, que reciben una cadena de entrada -En esta lección, deberás implementar dos funciones: is_palindrome()` y `is_not_palindrome()` - -1. Implementa la función `is_palindrome()`, que determina si una palabra es un palíndromo o no. Un palíndromo es una palabra que se lee igual en ambos sentidos. Las palabras pueden estar en cualquier caso, por lo que primero debes convertir la palabra a minúsculas: `word.lower()`. +1. Implementa la función `is_palindrome()`, que determina si una palabra es un palíndromo o no. Un palíndromo es una palabra que se lee igual en los dos sentidos. A la función se le pueden pasar palabras en cualquier caso, por eso primero hay que convertir la palabra a minúsculas: `word.lower()`. ```python is_palindrome('ala') # true is_palindrome('hexlet') # false - is_palindrome('radar') # true + is_palindrome('Radar') # true is_palindrome('función') # false ``` -2. Implementa la función `is_not_palindrome()`, que verifica que una palabra NO sea un palíndromo: +2. Implementa la función `is_not_palindrome()`, que comprueba que una palabra NO es un palíndromo: ```python - is_not_palindrome('radar') # false is_not_palindrome('ala') # false + is_not_palindrome('Radar') # false is_not_palindrome('hexlet') # true ``` - Para hacer esto, llama a la función `is_palindrome()` dentro de `is_not_palindrome()` y aplica la negación. + Para eso, llama a la función `is_palindrome()` dentro de `is_not_palindrome()` y aplica la negación. diff --git a/modules/45-logic/28-logical-negation/es/README.md b/modules/45-logic/28-logical-negation/es/README.md index c6c8ddf8..1601ecf2 100644 --- a/modules/45-logic/28-logical-negation/es/README.md +++ b/modules/45-logic/28-logical-negation/es/README.md @@ -1,32 +1,29 @@ - -Junto con los operadores lógicos **Y** y **O**, a menudo se utiliza la operación de "**negación**". Esta operación cambia el valor lógico al opuesto. En programación, la negación se representa con el operador unario `not`: +Junto con los operadores lógicos **Y** y **O**, se usa a menudo la operación de "**negación**". Cambia el valor lógico por el contrario. En Python a la negación le corresponde el operador unario `not` (en otros lenguajes `!`): ```python not True # False not False # True ``` -Por ejemplo, si tenemos una función que verifica si un número es par, podemos utilizar la negación para verificar si es impar: +Por ejemplo, si hay una función que comprueba la paridad de un número, con la negación se puede hacer la comprobación de la imparidad: ```python -def is_even(number): +def is_even(number: int) -> bool: return number % 2 == 0 print(is_even(10)) # => True print(not is_even(10)) # => False ``` -En el ejemplo anterior, agregamos `not` antes de llamar a la función y obtuvimos el resultado opuesto. - -La negación es una herramienta que nos permite expresar reglas deseadas en el código sin tener que escribir nuevas funciones. +En el ejemplo de arriba añadimos `not` a la izquierda de la llamada a la función y obtuvimos la acción inversa. -Incluso si escribimos `not not is_even`, el código funcionará de la siguiente manera: +La negación permite expresar en el código las reglas pensadas sin escribir funciones nuevas. Si se escribe `not not is_even(10)`, el código funcionará incluso en ese caso: ```python print(not not is_even(10)) # => True ``` -En lógica, la doble negación es equivalente a la ausencia de negación: +En lógica, la doble negación equivale a la ausencia de negación: ```python not not True # True @@ -36,4 +33,54 @@ print(not not is_even(10)) # => True print(not not is_even(11)) # => False ``` -Ahora sabes lo que significan los operadores **Y**, **O** y `not`. Con ellos, podrás crear condiciones compuestas a partir de dos o más expresiones lógicas. +`not` se puede combinar con `and` y `or`. Entre los operadores lógicos tiene la prioridad más alta, por eso se aplica primero: + +```python +not True or True # (not True) or True => False or True => True +not True and False # (not True) and False => False and False => False +``` + +Los paréntesis cambian el orden de evaluación: + +```python +not (True or True) # not True => False +not (True and False) # not False => True +``` + +Un ejemplo práctico: una función comprueba si el conductor puede ponerse al volante; hacen falta el carnet y la sobriedad: + +```python +def can_drive(has_license: bool, is_drunk: bool) -> bool: + return has_license and not is_drunk + +print(can_drive(True, False)) # => True (tiene carnet, sobrio) +print(can_drive(True, True)) # => False (tiene carnet, pero ebrio) +print(can_drive(False, False)) # => False (no tiene carnet) +``` + +Ahora sabes qué significan los operadores **Y**, **O** y `not`. Con su ayuda podrás fijar condiciones compuestas de dos o más expresiones lógicas. + +## Las leyes de De Morgan + +Al trabajar con expresiones lógicas complejas, a veces hace falta invertirlas o reescribirlas en una forma equivalente que sea más fácil de leer. Para eso existen las **leyes de De Morgan**: dos reglas que describen cómo se distribuye la negación por una expresión compuesta: + +```python +not (A and B) == not A or not B +not (A or B) == not A and not B +``` + +La primera ley: la negación de la conjunción es igual a la disyunción de las negaciones. Comprobémoslo: + +```python +not (True and False) # not False => True +not True or not False # False or True => True +``` + +La segunda ley: la negación de la disyunción es igual a la conjunción de las negaciones: + +```python +not (True or False) # not True => False +not True and not False # False and True => False +``` + +En la práctica, las leyes de De Morgan ayudan a simplificar las condiciones. Por ejemplo, en lugar de `not (is_admin or is_moderator)` se puede escribir `not is_admin and not is_moderator`, que se lee como "no es administrador y no es moderador". diff --git a/modules/45-logic/90-logical-expressions/es/EXERCISE.md b/modules/45-logic/90-logical-expressions/es/EXERCISE.md index 13ccd742..5c6414ec 100644 --- a/modules/45-logic/90-logical-expressions/es/EXERCISE.md +++ b/modules/45-logic/90-logical-expressions/es/EXERCISE.md @@ -1,5 +1,4 @@ - -Implementa la función `string_or_not()` que verifica si el parámetro proporcionado es una cadena correcta. Si lo es, devolverá como resultado `'yes'`, de lo contrario, devuelve `'no'` +Un formulario del sitio recibe datos del usuario. Antes de procesar el valor, el programa comprueba que en el campo del nombre se pasó una cadena y no un número u otro tipo. Implementa la función `string_or_not()`, que comprueba si el parámetro recibido es una cadena. Si lo es, devuelve `'yes'`; en caso contrario, `'no'` ```python string_or_not('Hexlet') # 'yes' @@ -8,7 +7,7 @@ string_or_not('') # 'yes' string_or_not(False) # 'no' ``` -Puedes verificar si el parámetro proporcionado es una cadena utilizando la función [isinstance()](https://docs.python.org/3/library/functions.html#isinstance): +Comprobar si el parámetro recibido es una cadena se puede hacer con la función [isinstance()](https://docs.python.org/3/library/functions.html#isinstance): ```python isinstance(3, str) # False diff --git a/modules/45-logic/90-logical-expressions/es/README.md b/modules/45-logic/90-logical-expressions/es/README.md index 67e0d1f0..9e1a6fde 100644 --- a/modules/45-logic/90-logical-expressions/es/README.md +++ b/modules/45-logic/90-logical-expressions/es/README.md @@ -1,180 +1,94 @@ - -En esta lección aprenderemos las reglas de transformación de argumentos y cómo trabajar con expresiones compuestas y doble negación. - -## Reglas de transformación - -El operador **OR** funciona de tal manera que se interrumpe la ejecución de izquierda a derecha y se devuelve el resultado del primer argumento que se pueda transformar en `True`. Si no hay ningún argumento que cumpla esta condición, se devuelve el último argumento, el de la derecha. - -Veamos un ejemplo: +En Python, los valores de distintos tipos se pueden usar directamente en expresiones lógicas. Al hacerlo se comportan como `True` o `False`, según pertenezcan a la categoría **truthy** o **falsy**. ```python -print(0 or 1) ## 1 -``` - -En este caso, el número `0` es equivalente a `False`, y el número `1` es equivalente a `True`. Por lo tanto, el operador **OR** devuelve `1`, ya que es el primer argumento que se puede transformar en `True`. +if "hello": + print("cadena no vacía — truthy") # se ejecutará -Veamos un ejemplo más complejo: - -```python -print(0 or False or '' or [] or 42 or "Hello") ## 42 +if not 0: + print("cero — falsy") # se ejecutará ``` -En este caso: - -- El número `0` es equivalente a `False` -- El valor `False` ya es `False` -- La cadena vacía (`''`) es equivalente a `False` -- La lista vacía (`[]`) es equivalente a `False` -- El número `42` es equivalente a `True` -- La cadena `"Hello"` también es equivalente a `True` +## Valores falsy y truthy -El operador **OR** verifica los valores de izquierda a derecha y devuelve el primer argumento que se pueda transformar en `True`. En este ejemplo, ese valor es el número `42`. +En Python hay un conjunto fijo de valores que se consideran **falsos** (`falsy`). A ellos pertenecen `0` y cualquier número nulo (`0.0`), la cadena vacía `""`, las colecciones vacías `[]`, `{}`, `set()`, `()` (todavía no las hemos visto) y también el valor especial `None`. -Ejemplo con el operador **AND**: +Todos los demás valores se consideran **verdaderos** (`truthy`). Son, por ejemplo, cualquier número distinto de cero (`1`, `-3.5`), una cadena no vacía (`"hi"`, `"0"`) y las colecciones no vacías (`[1]`, `{"a": 1}`, `(0,)`) (las estudiaremos en otros cursos de Hexlet). -```python -print(0 and 1) ## 0 -``` +## Cómo funcionan las expresiones lógicas -El operador **AND** funciona de tal manera que se interrumpe la ejecución de izquierda a derecha y se devuelve el resultado del primer argumento que se pueda transformar en `False`. Si no hay ningún argumento que cumpla esta condición, se devuelve el último argumento, el de la derecha. +En Python las expresiones lógicas no devuelven solo `True` o `False`. Devuelven uno de sus operandos. El operador `and` devuelve el primer operando falsy o el último truthy. El operador `or` devuelve el primer operando truthy o el último falsy. Por eso el resultado puede ser una cadena, un número o cualquier otro objeto que participe en la expresión. ```python -print(42 and "Hello" and [] and 0) ## [] -``` - -En este caso: - -- El número `42` es equivalente a `True` -- La cadena `"Hello"` es equivalente a `True` -- La lista vacía (`[]`) es equivalente a `False` -- El número `0` es equivalente a `False` - -El operador **AND** verifica los valores de izquierda a derecha y devuelve el primer argumento que se pueda transformar en `False`. En este ejemplo, ese valor es la lista vacía (`[]`). - -En Python hay dos reglas de transformación: - -- `0`, `0.0`, `''` y `None` se convierten en `False`. Estos valores se llaman **falsy**. También hay otros tipos de datos que aprenderemos en Hexlet. -- Todo lo demás se convierte en `True` +# and devuelve el primer falsy o el último operando +print("hello" and "world") # => "world" (los dos truthy — el último) +print("" and "world") # => "" (el primer falsy) +print(0 and "world") # => 0 (el primer falsy) -Estas reglas se utilizan en el desarrollo, por ejemplo, para definir un valor por defecto: - -```python -value = name or '' -# Ejemplos -234 or '' # 234 -'hexlet' or '' # 'hexlet' -None or '' # '' +# or devuelve el primer truthy o el último operando +print("hello" or "world") # => "hello" (el primer truthy) +print("" or "world") # => "world" (el primer truthy) +print("" or 0) # => 0 (los dos falsy — el último) ``` -Si `nombre` tiene uno de los valores falsy, se asignará una cadena vacía a la variable `valor`. En este caso, en el código posterior podremos trabajar con `valor` como si fuera una cadena. +El orden de evaluación depende de la prioridad de los operadores. -Pero aquí hay un posible error. Si `nombre` contiene un valor falsy y se puede asignar a la variable `valor` un valor como `0`, `False`, `None`, entonces el código anterior no funcionará correctamente: +```text +Prioridad (de la más alta a la más baja): -```python -# El valor realmente existe, -# pero es Falsy, por lo que no se selecciona en la condición OR -False or '' # '' -0 or '' # '' -None or '' # '' + () paréntesis + ↓ + not negación + ↓ + and Y lógico + ↓ + or O lógico ``` -## Expresiones compuestas - -Si se combinan expresiones lógicas entre sí, se pueden obtener formas interesantes de resolver problemas con código. - -Supongamos que queremos implementar un código en el que una variable se asigna: - -- La cadena `yes` si el número es par -- La cadena `no` si el número es impar - -Esto se puede hacer utilizando los conocimientos que hemos adquirido anteriormente: +## Ejemplo con el resto de la división ```python -# el número es par -resultado = 10 % 2 == 0 and 'yes' or 'no' # 'yes' -# o imprimir directamente en la pantalla -print(10 % 2 == 0 and 'yes' or 'no') # => 'yes' -# el número es impar -print(11 % 2 == 0 and 'yes' or 'no') # => 'no' +result = 10 % 2 == 0 and "yes" or "no" +print(result) # => "yes" ``` -Estas expresiones funcionan según el orden y la prioridad. La asignación tiene la prioridad más baja, por lo que ocurre al final. La comparación `==` tiene una prioridad más alta que los operadores lógicos `and` y `or`, por lo que se realiza antes. Luego, el código se ejecuta de izquierda a derecha, ya que la prioridad de `and` es mayor que la de `or`. Veamos los pasos: - -```python -# Para un número par -# 1 paso -10 % 2 == 0 # True -# 2 paso -True and 'yes' # Resultado: verdadero -# La comprobación de or se realiza, pero la parte derecha no se ejecuta, ya que se devuelve inmediatamente 'yes' - -# Para un número impar -# 1 paso -11 % 2 == 0 # False -# 2 paso -False and 'yes' # Resultado: falso, se sigue comprobando -# 3 paso -False or 'no' # Se selecciona y devuelve 'no' -``` +Analicemos esa expresión paso a paso. Primero se ejecuta la aritmética `10 % 2 == 0` → `True`. Después actúa `and`: como a la izquierda hay verdad, el resultado pasa a ser `"yes"`. Como `"yes"` es truthy, el operador `or` devuelve precisamente ese valor. -Se puede utilizar el mismo esquema con cualquier expresión al principio: +El mismo principio dentro de una función: ```python -print(somefunc() and 'yes' or 'no') -``` +def parity(number: int) -> str: + return number % 2 == 0 and "even" or "odd" -## Doble Negación - -Recordemos cómo se ve una operación de negación: - -```python -answer = True -print(not answer) # => False +print(parity(10)) # => "even" +print(parity(7)) # => "odd" ``` -Con una doble negación, el valor final es igual al valor inicial: +## Ejemplos ```python -answer = True -print(not not answer) # => True -``` +print(7 % 2 == 0 and "even" or "odd") +# => "odd" -El operador `not` siempre devuelve un valor booleano, independientemente del tipo de argumento proporcionado, y no cambia el valor al opuesto. Por lo tanto, la doble negación también devolverá un Verdadero/Falso booleano. +print(("" and "not empty") or "empty") +# => "empty" -```python -answer = 'python' -print(not answer) # => False -print(not not answer) # => True -``` - -## Error de Elección - -Imagina que necesitamos comprobar si el valor es igual a uno u otro. Por ejemplo, la variable `value` debe contener uno de dos valores: `first` o `second`. Los desarrolladores novatos a veces escriben esta expresión de esta manera: +print(("hello" and "not empty") or "empty") +# => "not empty" -```python -value == ('first' or 'second') +print((-5 > 0 and "positive") or "non-positive") +# => "non-positive" ``` -Sin embargo, este código dará un resultado incorrecto. Debes recordar la prioridad de las operaciones. Primero, se evalúa todo lo que está dentro de los paréntesis: `'first' or 'second'`. Si ejecutas este código en Replit, la salida será la siguiente: - -```bash -python -Python 3.8.2 (default, Apr 12 2020, 15:53:37) ->>> 'first' or 'second' -'first' ->>> -``` +## Uso en funciones -Ahora reemplacemos la expresión original con la expresión parcialmente evaluada: +La particularidad de los valores truthy y falsy resulta cómoda de aplicar en las funciones. Por ejemplo, se puede escribir una función que devuelve un texto si no está vacío, o un valor por defecto si la cadena está vacía. ```python -value == 'first' -``` - -No es en absoluto lo que esperábamos. Y ahora volvamos al inicio y escribamos la verificación correctamente: +def get_text_or_default(text: str, default: str = "empty") -> str: + return text or default -```python -# No es necesario colocar paréntesis, -# porque la prioridad de == es mayor que la de or -value == 'first' or value == 'second' +print(get_text_or_default("hello")) # => "hello" +print(get_text_or_default("")) # => "empty" ``` + +Aquí la expresión `text or default` funciona así: si `text` no está vacío (truthy), la función lo devolverá. Si `text` está vacío (falsy), la función devolverá `default`. diff --git a/modules/48-conditionals/30-if/es/EXERCISE.md b/modules/48-conditionals/30-if/es/EXERCISE.md index 5aa22ba6..280252f5 100644 --- a/modules/48-conditionals/30-if/es/EXERCISE.md +++ b/modules/48-conditionals/30-if/es/EXERCISE.md @@ -1,5 +1,4 @@ - -Implementa la función `guess_number()`, que toma un número y verifica si es igual al número dado (supongamos que es 42). Si es igual, la función debe devolver la cadena `'You win!'`, de lo contrario, debe devolver la cadena `'Try again!'`. +En un concurso se ha pensado el número 42. El participante dice un número y el programa le contesta de inmediato si acertó o no. Implementa la función `guess_number()`, que recibe un número y comprueba si es igual a 42. Si es igual, devuelve `'You win!'`; en caso contrario, `'Try again!'`. ```python guess_number(42) # You win! diff --git a/modules/48-conditionals/30-if/es/README.md b/modules/48-conditionals/30-if/es/README.md index 3fd86af4..04991af8 100644 --- a/modules/48-conditionals/30-if/es/README.md +++ b/modules/48-conditionals/30-if/es/README.md @@ -1,21 +1,77 @@ +Las expresiones lógicas permiten comprobar distintas condiciones. Pero por sí solas únicamente devuelven `True` o `False`. Para que el programa pueda ejecutar acciones distintas según el resultado, en Python existe la construcción especial `if`. -En esta lección aprenderás que con las estructuras condicionales puedes cambiar el comportamiento de un programa dependiendo de las condiciones que se evalúen. Esto te permitirá escribir programas complejos que se comporten de manera diferente según la situación. +![Construcción condicional if](./assets/if-statement.png) -Como ejemplo, consideremos una función que determina el tipo de una oración dada. Al principio, esta distinguirá entre oraciones normales y preguntas: +```python +if 5 > 3: + print("Yes, it is true") +``` + +Aquí la cadena `"Yes, it is true"` se imprimirá, porque la condición `5 > 3` es verdadera. + +```text +┌───────────┐ +│ ¿condición?│ +└─────┬─────┘ + True │ + ↓ +┌───────────┐ +│ cuerpo if │ +└───────────┘ +``` + +## Las sangrías en los bloques + +Después de la palabra `if` se escribe la condición, luego se ponen dos puntos y empieza el bloque de código con sangría. Todas las líneas con la misma sangría forman parte de un mismo bloque. + +```python +if 10 == 10: + print("First") + print("Second") + +print("Goodbye!") +``` + +Aquí se imprimirán `"First"` y `"Second"`, porque la condición se cumplió. Y `"Goodbye!"` se imprimirá en cualquier caso, ya que está fuera de los límites del bloque. El principio es el mismo que en la definición de funciones. + +## Uso de if dentro de una función + +Veamos una función que determina el tipo de la oración recibida. Si termina en un signo de interrogación, la función devolverá `"question"`; en caso contrario devolverá `"normal"`. ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] if last_char == '?': return 'question' return 'normal' print(get_type_of_sentence('Hodor')) # => normal -print(get_type_of_sentence('Hodor?')) # => pregunta +print(get_type_of_sentence('Hodor?')) # => question +``` + +Aquí se usan a la vez dos `return`. Si la condición dentro del `if` se cumple, actúa `return 'question'` y la función termina. Si la condición no se cumple, el control pasa a la línea siguiente con `return 'normal'`. + +Así pues, la función tiene varios puntos de salida posibles. Es una práctica frecuente. Según las condiciones, la función puede terminar de distintas maneras. + +A pesar de que la función `get_type_of_sentence` usa `if`, devuelve cadenas, y eso significa que no es un predicado. Como predicado veamos una función que comprueba si hay suficiente dinero para la compra. + +```python +def has_enough_money(balance: int, price: int) -> bool: + if balance >= price: + return True + return False + +print(has_enough_money(100, 50)) # => True +print(has_enough_money(30, 50)) # => False ``` -`if` es una estructura del lenguaje que controla el orden de ejecución de las instrucciones. Después de la palabra `if`, se le pasa una expresión booleana y se coloca dos puntos al final. Luego se describe un bloque de código. Este se ejecutará si el predicado es verdadero. +## if y las expresiones lógicas -Si el predicado es falso, se omitirá el bloque de código y la función continuará su ejecución. En nuestro caso, la siguiente línea de código, `return 'normal'`, hará que la función devuelva la cadena y termine. +La función `has_enough_money` la escribimos con `if`. Pero en esa forma podría prescindir de él, porque el resultado de la comparación es ya por sí mismo una expresión lógica. + +```python +def has_enough_money(balance: int, price: int) -> bool: + return balance >= price +``` -`return` puede estar en cualquier lugar de la función, incluso dentro de un bloque de código con una condición. +En los casos simples es mejor devolver esa expresión de inmediato. El `if` hace falta allí donde dentro del bloque se ejecutan acciones adicionales además de devolver el resultado. Cuanto más complejos sean los programas que escribamos, más a menudo empezarán a aparecer esas situaciones. diff --git a/modules/48-conditionals/30-if/es/assets/if-statement.png b/modules/48-conditionals/30-if/es/assets/if-statement.png new file mode 100644 index 00000000..713c6a37 Binary files /dev/null and b/modules/48-conditionals/30-if/es/assets/if-statement.png differ diff --git a/modules/48-conditionals/40-if-else/es/EXERCISE.md b/modules/48-conditionals/40-if-else/es/EXERCISE.md index 5537e50a..d2139943 100644 --- a/modules/48-conditionals/40-if-else/es/EXERCISE.md +++ b/modules/48-conditionals/40-if-else/es/EXERCISE.md @@ -1,9 +1,8 @@ +Implementa la función `normalize_url()`, que realiza la normalización de los datos. Recibe la dirección de un sitio y la devuelve con `https://` al principio. -Implementa la función `normalize_url()`, que realiza la normalización de los datos. Recibe una dirección web y devuelve la misma con `https://` al principio. +La función recibe direcciones en forma de `DIRECCIÓN` o `http://DIRECCIÓN`, pero siempre devuelve la dirección en forma de `https://DIRECCIÓN`. A la entrada de la función también puede llegar una dirección ya normalizada, `https://DIRECCIÓN`; en ese caso no hay que cambiar nada. -La función acepta direcciones en forma de `DIRECCIÓN` o `http://DIRECCIÓN`, pero siempre devuelve la dirección en forma de `https://DIRECCIÓN`. También puede recibir una dirección ya normalizada `https://DIRECCIÓN`, en cuyo caso no se debe hacer ningún cambio. - -Ejemplos de uso: +Ejemplos de llamadas: ```python print(normalize_url('https://ya.ru')) # => 'https://ya.ru' @@ -11,18 +10,18 @@ print(normalize_url('google.com')) # => 'https://google.com' print(normalize_url('http://ai.fi')) # => 'https://ai.fi' ``` -Hay varias formas de resolver este problema. Una de ellas es comparar los primeros 7 caracteres de la cadena de argumento con la cadena `http://` y luego agregar o no agregar `https://` en base a eso. +Hay varias formas de resolver la tarea. Una de ellas es comparar los primeros 7 caracteres de la cadena-argumento con la cadena `http://` y luego, a partir de eso, añadirle o no `https://`. -También es probable que necesites descartar la parte innecesaria al principio de la cadena. Recuerda que vimos cómo obtener una parte de una cadena usando el slicing. Si no lo recuerdas, aquí tienes un recordatorio: +También es probable que necesites descartar la parte innecesaria del principio de la cadena. ¿Recuerdas que vimos la forma de obtener un trozo de una cadena con un corte? Si no, te lo recuerdo: ```python -# Tomamos los primeros 6 caracteres -print('Invernalia'[:6]) # => 'Inver' +# Tomamos 2 caracteres desde el principio +print('python'[:2]) # => 'py' ``` -Así es, con el slicing también puedes descartar un número determinado de caracteres: +Pues bien, con los cortes también se puede descartar una cantidad determinada de caracteres: ```python -# Descartamos los primeros 6 caracteres -print('Winterfell'[6:]) # => 'Winter' +# Descartamos los 2 primeros caracteres +print('python'[2:]) # => 'thon' ``` diff --git a/modules/48-conditionals/40-if-else/es/README.md b/modules/48-conditionals/40-if-else/es/README.md index e818532e..b1ec01bb 100644 --- a/modules/48-conditionals/40-if-else/es/README.md +++ b/modules/48-conditionals/40-if-else/es/README.md @@ -1,8 +1,7 @@ - -Observa la función siguiente. Determina el tipo de oración por su último carácter: si la oración termina con un signo de interrogación, la función devuelve `Sentence is question`; de lo contrario, devuelve `Sentence is normal`: +Mira la función de abajo. Determina el tipo de la oración por su último carácter: si la oración termina en un signo de interrogación, la función devolverá `Sentence is question`; en caso contrario, `Sentence is normal`: ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] if last_char == '?': @@ -12,13 +11,24 @@ def get_type_of_sentence(sentence): return "Sentence is " + sentence_type -print(get_type_of_sentence('Hodor')) # => 'Sentence is normal' -print(get_type_of_sentence('Hodor?')) # => 'Sentence is question' +print(get_type_of_sentence('Hodor')) # => Sentence is normal +print(get_type_of_sentence('Hodor?')) # => Sentence is question ``` -Hemos añadido un `else` y un nuevo bloque. Se ejecutará si la condición en el `if` es falsa. También se pueden anidar otras condiciones `if` dentro del bloque `else`. "Else" se traduce como "en otro caso" o "de lo contrario". +Añadimos `else` y un bloque nuevo. Se ejecutará si la condición del `if` resulta falsa. Además, dentro del bloque `else` se pueden anidar otras condiciones `if`. Else se traduce como "si no", "en otro caso". + +```text + ┌───────────┐ + │ ¿condición?│ + └─────┬─────┘ + True │ │ False + ↓ ↓ +┌──────────┐ ┌───────────┐ +│ cuerpo if│ │cuerpo else│ +└──────────┘ └───────────┘ +``` -Ejemplo de bloques anidados: +Un ejemplo de bloques anidados: ```python number = 10 @@ -32,10 +42,10 @@ else: print("Number is less than 10") ``` -Se pueden estructurar las construcciones `if-else` de dos formas. Usando la negación se puede cambiar el orden de los bloques: +La construcción `if-else` se puede escribir de dos formas. Con la negación se puede cambiar el orden de los bloques: ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] if last_char != '?': @@ -46,4 +56,39 @@ def get_type_of_sentence(sentence): return "Sentence is " + sentence_type ``` -Para facilitar la estructura de la construcción, intenta elegir una comprobación sin negación y ajusta el contenido de los bloques en consecuencia. +Para que la construcción sea más fácil de escribir, procura elegir la comprobación sin negaciones y ajusta el contenido de los bloques a ella. + +Con el ejemplo del uso de `else` se ve lo importante que es no olvidarse de separar los bloques. + +```python +# Incorrecto +def check_number(number): + if number > 0: + print("El número es positivo") + if number > 10: + print("El número es mayor que 10") + else: + print("El número no es positivo") + +check_number(3) +# => El número es positivo +# => El número no es positivo +``` + +En el ejemplo de arriba nos olvidamos de "anidar" con sangría el segundo `if`, por eso el `else` ahora se refiere al segundo `if`. + +```python +# Correcto +def check_number(number: int) -> None: + if number > 0: + print("El número es positivo") + if number > 10: + print("El número es mayor que 10") + else: + print("El número no es positivo") + +check_number(3) +# => El número es positivo +``` + +Ahora el segundo `if` está anidado en el primero, y el `else` está al mismo nivel que el primero y se opone a él. diff --git a/modules/48-conditionals/50-else-if/es/README.md b/modules/48-conditionals/50-else-if/es/README.md index faa78c34..eb943606 100644 --- a/modules/48-conditionals/50-else-if/es/README.md +++ b/modules/48-conditionals/50-else-if/es/README.md @@ -1,8 +1,7 @@ - -La función `get_type_of_sentence()` solo distingue entre oraciones interrogativas y oraciones normales. Agregaremos soporte para oraciones exclamativas: +La función `get_type_of_sentence()` distingue solo las oraciones interrogativas y las normales. Añadámosle el soporte de las oraciones exclamativas: ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] if last_char == '?': @@ -13,22 +12,19 @@ def get_type_of_sentence(sentence): else: sentence_type = 'normal' - return 'La oración es ' + sentence_type + return 'Sentence is ' + sentence_type -print(get_type_of_sentence('¿Quién?')) # => 'La oración es pregunta' -print(get_type_of_sentence('No')) # => 'La oración es normal' -print(get_type_of_sentence('¡No!')) # => 'La oración es exclamación' +print(get_type_of_sentence('Who?')) # => 'Sentence is normal' +print(get_type_of_sentence('No')) # => 'Sentence is normal' +print(get_type_of_sentence('No!')) # => 'Sentence is exclamation' ``` -Hemos agregado la verificación de oraciones exclamativas: 'exclamación'. Técnicamente, esta función funciona, pero interpreta incorrectamente las oraciones interrogativas. También tiene problemas desde el punto de vista semántico: - -* Se verifica la presencia del signo de exclamación de todos modos, incluso si ya se ha detectado el signo de interrogación. -* La rama `else` está descrita para la segunda condición, pero no para la primera. Por lo tanto, la oración interrogativa se convierte en "normal". +Añadimos la comprobación de las oraciones exclamativas. Técnicamente esa función funciona, pero interpreta mal las oraciones interrogativas. Además tiene problemas desde el punto de vista de la semántica. La presencia del signo de exclamación se comprueba en cualquier caso, incluso si ya se detectó un signo de interrogación. La rama `else` está descrita para la segunda condición, pero no para la primera. Por eso una oración interrogativa acaba siendo `"normal"`. -Para corregir la situación, utilizaremos otra posibilidad de la construcción condicional: +Para arreglar la situación, aprovechemos otra posibilidad de la construcción condicional: ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] if last_char == '?': @@ -38,17 +34,31 @@ def get_type_of_sentence(sentence): else: sentence_type = 'normal' - return 'La oración es ' + sentence_type + return 'Sentence is ' + sentence_type -print(get_type_of_sentence('¿Quién?')) # => 'La oración es pregunta' -print(get_type_of_sentence('No')) # => 'La oración es normal' -print(get_type_of_sentence('¡No!')) # => 'La oración es exclamación' +print(get_type_of_sentence('Who?')) # => 'Sentence is question' +print(get_type_of_sentence('No')) # => 'Sentence is normal' +print(get_type_of_sentence('No!')) # => 'Sentence is exclamation' ``` -Ahora todas las condiciones se han unificado en una sola construcción. `elif` significa "si la condición anterior no se cumple, pero la actual sí". La estructura resultante es la siguiente: +Ahora todas las condiciones se alinearon en una construcción única. La palabra clave `elif` significa "si no se cumplió la condición anterior, pero se cumple la actual". + +```text + ┌─────────────────┐ + │ ¿condición 1? │ + └────┬────────┬───┘ + True │ │ False + ↓ ↓ +┌──────────┐ ┌─────────────────┐ +│ cuerpo if│ │ ¿condición 2? │ +└──────────┘ └────┬────────┬───┘ + True │ │ False + ↓ ↓ + ┌───────────┐ ┌───────────┐ + │cuerpo elif│ │cuerpo else│ + └───────────┘ └───────────┘ +``` -* si la última letra es `?`, entonces `'pregunta'` -* si la última letra es `!`, entonces `'exclamación'` -* para cualquier otro caso, `'normal'` +La lógica de la función está montada así. Si la última letra es `?`, se devuelve `'question'`. Si la última letra es `!`, se devuelve `'exclamation'`. En todos los demás casos se devuelve `'normal'`. -Solo se ejecutará uno de los bloques de código que pertenezca a toda la construcción `if`. +Se ejecutará solo uno de los bloques de código que pertenecen a toda la construcción `if`. diff --git a/modules/48-conditionals/60-ternary-operator/es/EXERCISE.md b/modules/48-conditionals/60-ternary-operator/es/EXERCISE.md index da48cc57..cc4e1afd 100644 --- a/modules/48-conditionals/60-ternary-operator/es/EXERCISE.md +++ b/modules/48-conditionals/60-ternary-operator/es/EXERCISE.md @@ -1,11 +1,10 @@ - -Implemente la función `flip_flop()`, que recibe una cadena como entrada y, si esa cadena es igual a `'flip'`, devuelve la cadena `'flop'`. De lo contrario, la función debe devolver `'flip'`. +Un botón de la aplicación funciona como un interruptor: cada pulsación cambia el estado al contrario. Implementa la función `flip_flop()`, que recibe de entrada una cadena y, si esa cadena es igual a `'flip'`, devuelve la cadena `'flop'`. En caso contrario, la función debe devolver `'flip'`. Ejemplos de llamadas: ```python -print(flip_flop('flip')) # => 'flop' -print(flip_flop('flop')) # => 'flip' +print(flip_flop('flip')) # => flop +print(flip_flop('flop')) # => flip ``` -Intente escribir dos versiones de la función: una con un if-else normal y otra con un operador ternario. +Intenta escribir dos versiones de la función: con un if-else normal y con el operador ternario. diff --git a/modules/48-conditionals/60-ternary-operator/es/README.md b/modules/48-conditionals/60-ternary-operator/es/README.md index 19498830..abaf9dcb 100644 --- a/modules/48-conditionals/60-ternary-operator/es/README.md +++ b/modules/48-conditionals/60-ternary-operator/es/README.md @@ -1,28 +1,30 @@ - -Eche un vistazo a la definición de una función que devuelve el módulo de un número dado: +Mira la definición de la función que devuelve el valor absoluto del número recibido: ```python -def abs(number): +# El valor absoluto es el número mismo sin el signo +def abs(number: int) -> int: if number >= 0: return number return -number ``` -Pero se puede escribir de manera más concisa. Para ello, a la derecha de `return` debe haber una expresión, pero `if` es una instrucción y no una expresión. En Python existe una construcción que funciona como un `if-else`, pero se considera una expresión. Se llama **operador ternario** y es el único operador en Python que requiere tres operandos: +Pero se puede escribir de forma más concisa. En Python hay una construcción que funciona como `if-else`. Se llama **operador ternario** y es el único operador de Python que exige tres operandos: ```python -def abs(number): +def abs(number: int) -> int: return number if number >= 0 else -number ``` El patrón general se ve así: ` if else `. -Vamos a reescribir la versión inicial de `get_type_of_sentence()` de manera similar. +![Operador ternario](./assets/ternary.png) + +Reescribamos la variante inicial de `get_type_of_sentence()` de forma análoga. Antes: ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] if last_char == '?': return 'question' @@ -32,7 +34,7 @@ def get_type_of_sentence(sentence): Después: ```python -def get_type_of_sentence(sentence): +def get_type_of_sentence(sentence: str) -> str: last_char = sentence[-1] return 'question' if last_char == '?' else 'normal' @@ -40,4 +42,4 @@ print(get_type_of_sentence('Hodor')) # => normal print(get_type_of_sentence('Hodor?')) # => question ``` -Se puede anidar un operador ternario dentro de otro operador ternario. Pero no se recomienda hacerlo, ya que ese código es difícil de leer y depurar. +Un operador ternario se puede anidar dentro de otro operador ternario. Pero eso se considera una mala práctica: ese código es muy difícil de entender. diff --git a/modules/48-conditionals/60-ternary-operator/es/assets/ternary.png b/modules/48-conditionals/60-ternary-operator/es/assets/ternary.png new file mode 100644 index 00000000..146d4e31 Binary files /dev/null and b/modules/48-conditionals/60-ternary-operator/es/assets/ternary.png differ diff --git a/modules/48-conditionals/65-match/es/README.md b/modules/48-conditionals/65-match/es/README.md index 74b388f1..f8cd35f7 100644 --- a/modules/48-conditionals/65-match/es/README.md +++ b/modules/48-conditionals/65-match/es/README.md @@ -1,60 +1,63 @@ - -Muchos lenguajes, además de la estructura condicional `if`, incluyen `switch`. Con la versión 3.10 de Python, se agregó el operador `match` con funcionalidad similar. En esta lección, aprenderemos sobre este operador. - -El operador `match` es una versión especializada de `if` creada para situaciones específicas. Por ejemplo, se debe utilizar cuando hay una cadena de `if else` con comprobaciones de igualdad: +El operador `match` es una versión especializada de `if`, creada para situaciones particulares. Por ejemplo, hay que usarla allí donde hay una cadena de `if else` con comprobaciones de igualdad: ```python if status == 'processing': - # Hacer algo + # Hacemos una cosa elif status == 'paid': - # Hacer algo más + # Hacemos otra elif status == 'new': - # Hacer algo diferente + # Hacemos una tercera else: - # Hacer algo más + # Hacemos una cuarta ``` -Esta comprobación compuesta tiene una característica distintiva: cada rama es una comprobación del valor de la variable `status`. El operador `match` permite escribir este código de forma más corta y expresiva: +Esa comprobación compuesta tiene un rasgo distintivo. Cada rama aquí es una comprobación del valor de la variable `status`. El operador `match` permite escribir ese código de forma más corta y expresiva. ```python match status: case 'processing': # status == 'processing' - # Hacer algo + # Hacemos una cosa case 'paid': # status == 'paid' - # Hacer algo más + # Hacemos otra case 'new': # status == 'new' - # Hacer algo diferente + # Hacemos una tercera case _: # else - # Hacer algo más + # Hacemos una cuarta ``` -En términos de cantidad de elementos, `match` es una construcción compleja. Está compuesta por los siguientes elementos: +```text +match valor: + │ + ├── case 'a' → bloque 1 + ├── case 'b' → bloque 2 + ├── case 'c' → bloque 3 + └── case _ → bloque por defecto +``` -* Una descripción externa que incluye la palabra clave `match`. Esta es la variable cuyos valores determinarán el comportamiento del `match`. -* Las construcciones `case`, dentro de las cuales se describe el comportamiento para diferentes valores de la variable considerada. Cada `case` corresponde a un `if` en el ejemplo anterior. Además, `case _` es una situación especial que corresponde a la rama `else` en las estructuras condicionales. Al igual que `else`, no es obligatorio especificar `case _`. +Desde el punto de vista de la cantidad de elementos, `match` es una construcción compleja. La descripción externa incluye la palabra clave `match` y la variable según cuyos valores `match` elegirá el comportamiento. Dentro se sitúan las construcciones `case`, cada una de las cuales describe el comportamiento para uno de los valores de la variable. Cada `case` corresponde a un `if` del ejemplo de arriba. Además, `case _` es una situación especial que corresponde a la rama `else` de las construcciones condicionales. Igual que `else`, indicar `case _` no es obligatorio. -Dentro de `match`, solo se permite la sintaxis mostrada anteriormente. En otras palabras, se pueden utilizar `case`. Sin embargo, dentro de cada `case`, la situación es diferente. Aquí se puede ejecutar cualquier código arbitrario: +Dentro de `match` solo se admite la sintaxis mostrada arriba. En otras palabras, allí se puede usar `case`. Pero dentro de cada `case` la situación es distinta. Aquí se puede ejecutar cualquier código arbitrario. ```python match count: case 1: - # Hacer algo útil + # Hacemos algo útil case 2: - # Hacer algo útil + # Hacemos algo útil case _: - # Hacer algo + # Hacemos algo ``` -A veces, el resultado obtenido dentro de `case` es el final de la ejecución de la función que contiene el `match`. En ese caso, es necesario devolverlo de alguna manera. Hay dos formas de resolver este problema: +A veces el resultado obtenido dentro de un `case` termina la ejecución de la función que contiene el `match`. En ese caso hay que devolverlo de alguna manera hacia fuera. Para resolver esa tarea hay dos formas. -Primera forma: crear una variable antes del `match`, asignarle un valor dentro de `case` y luego devolver el valor de esa variable al final: +La primera. Crear una variable antes del `match`, rellenarla en el `case` y luego, al final, devolver el valor de esa variable hacia fuera. ```python def count_items(count): - # Declarar una variable + # Declaramos la variable result = '' - # Llenar el espacio + # La rellenamos match count: case 1: result = 'one' @@ -63,11 +66,11 @@ def count_items(count): case _: result = None - # Devolver el valor + # La devolvemos return result ``` -La segunda forma es más simple y corta. En lugar de crear una variable al trabajar con `case`, se puede hacer un retorno normal de la función: +La segunda forma es más simple y más corta. En lugar de crear una variable, al trabajar con `case` se puede hacer un retorno normal desde la función. ```python def count_items(count): @@ -80,4 +83,4 @@ def count_items(count): return None ``` -Aunque el operador `match` se encuentra en el código, técnicamente siempre se puede prescindir de él. La ventaja clave de su uso es que expresa mejor la intención del programador cuando se necesitan comprobar valores específicos de una variable. Aunque el código es un poco más largo físicamente, es más fácil de leer en comparación con los bloques `elif`. +El operador `match`, aunque aparece en el código, técnicamente siempre se puede evitar. La utilidad clave de usarlo está en que expresa mejor la intención del programador cuando hay que comprobar valores concretos de una variable. Aunque el código haya crecido físicamente un poco, es más fácil de leer, a diferencia de los bloques `elif`. diff --git a/modules/50-loops/10-while/es/EXERCISE.md b/modules/50-loops/10-while/es/EXERCISE.md index a0905d91..58e34c5b 100644 --- a/modules/50-loops/10-while/es/EXERCISE.md +++ b/modules/50-loops/10-while/es/EXERCISE.md @@ -1,18 +1,12 @@ +Escribe la función `print_countdown()`, que imita una cuenta atrás antes del arranque. La función recibe la cantidad de segundos e imprime los números desde ese valor hasta `1`. -Implementa la función `print_countdown()`, que imita una cuenta regresiva antes de un lanzamiento. La función recibe la cantidad de segundos e imprime los números desde ese valor hasta `1`. - -Después de terminar la cuenta regresiva, la función debe imprimir `Go!`. - -Ejemplo de llamada y salida: +Después de terminar la cuenta, la función debe mostrar en pantalla la cadena `Go!`. ```python print_countdown(4) -``` - -```text -4 -3 -2 -1 -Go! +# => 4 +# => 3 +# => 2 +# => 1 +# => Go! ``` diff --git a/modules/50-loops/10-while/es/README.md b/modules/50-loops/10-while/es/README.md index 995ad5f2..99c120ec 100644 --- a/modules/50-loops/10-while/es/README.md +++ b/modules/50-loops/10-while/es/README.md @@ -1,119 +1,97 @@ +Además de las construcciones condicionales, en programación es imposible arreglárselas sin los bucles. Es un mecanismo especial que permite ejecutar cualquier acción muchas veces. Sobre su base se construyen prácticamente todos los cálculos, desde el recuento de la nota media de un grupo hasta el procesamiento de las peticiones entrantes en los sitios web. -Los programas que escribimos en el curso se vuelven más complejos y extensos. Aunque todavía están lejos de ser programas reales, ya nos hacen esforzarnos un poco más. +![Bucle while](./assets/while.png) -En esta lección, nos adentraremos en uno de los temas básicos más difíciles de la programación: los **ciclos**. +El bucle guarda la acción repetida en un solo lugar y la lanza de nuevo mientras la condición siga siendo verdadera. -Las aplicaciones de software ayudan a gestionar empleados, finanzas y pueden ser entretenidas. A pesar de las diferencias, todas ellas ejecutan algoritmos incorporados en ellas, que son similares. Un **algoritmo** es una secuencia de acciones que conduce a un resultado esperado. +## El primer ejemplo -Imaginemos que tenemos un libro y queremos encontrar una frase específica en él. Recordamos la frase, pero no sabemos en qué página está. Tendremos que revisar las páginas secuencialmente hasta encontrarla. Este proceso se llama algoritmo. +Supongamos que el programa debe mostrar cinco veces la cadena `"Hello!"`. Para detener la repetición en el momento necesario, el programa necesita una variable que guarde el número del paso actual. Esa variable normalmente se llama contador. -Un algoritmo implica comprobaciones lógicas y recorrer las páginas. No se sabe de antemano cuántas páginas habrá que revisar, pero el proceso de revisión se repite de la misma manera. Para realizar acciones repetitivas, se necesitan ciclos. Cada repetición se llama **iteración**. - -Escribamos una función con un ciclo simple que imprima la cadena `'Hello!'` `n` veces en la pantalla: +En el ejemplo el contador se llama `counter`. Antes del bucle es igual a `0`. Después de cada salida de la cadena lo aumentamos en uno. ```python -def print_hello(n): - counter = 0 - while counter < n: - print('Hello!') - counter = counter + 1 +counter = 0 +while counter < 5: + print("Hello!") + counter = counter + 1 -print_hello(2) +# => Hello! +# => Hello! +# => Hello! # => Hello! # => Hello! ``` -Ahora analicemos un ejemplo de una función con un ciclo que imprime los números del 1 al número pasado como argumento: +Ahora el bucle puede comprobar el valor del contador antes de cada repetición. Mientras `counter < 5`, se ejecuta el código con sangría bajo la línea `while`. Ese bloque se llama cuerpo del bucle. -```python -print_numbers(3) -# => 1 -# => 2 -# => 3 -``` +Después de ejecutar el cuerpo, el intérprete vuelve a la condición y la comprueba de nuevo. Mientras la condición sea verdadera, el bucle continúa. Cuando la condición se vuelve falsa (`False`), el programa sale del bucle y ejecuta el código siguiente. -No se puede implementar esta función con los medios que ya hemos aprendido, ya que no se conoce de antemano la cantidad de impresiones en pantalla. Pero con los ciclos no hay problemas: +Sin modificar el contador, la condición nunca se volverá falsa y el bucle se convertirá en infinito. Desde fuera parece como si el programa se hubiera colgado. -```python -def print_numbers(last_number): - # i es una abreviatura de índice - # se utiliza por convención en muchos lenguajes - # como contador del ciclo - i = 1 - while i <= last_number: - print(i) - i = i + 1 - print('finished!') +## El funcionamiento del bucle paso a paso -print_numbers(3) -# => 1 -# => 2 -# => 3 -# => finished! -``` +Antes de la primera repetición, `counter` es igual a `0`. -El ciclo `while` consta de tres elementos: +**Paso 1.** El intérprete comprueba `counter < 5`. El valor `0` es menor que `5`, por eso se ejecuta el cuerpo del bucle. +En la pantalla se muestra `Hello!` y `counter` aumenta hasta `1`. -* La palabra clave `while` -* El predicado: una condición que se especifica después de `while` y se evalúa en cada iteración -* El bloque de código: el cuerpo del ciclo +**Paso 2.** El intérprete comprueba de nuevo la condición. El valor `1` sigue siendo menor que `5`, por eso el cuerpo del bucle se ejecuta una vez más. +En la pantalla se muestra otra vez `Hello!` y `counter` aumenta hasta `2`. -Cada ejecución del cuerpo se llama **iteración**. En el ejemplo anterior, `print_numbers(3)` provocó tres iteraciones, en cada una de las cuales se imprimió la variable `i` en la pantalla. La construcción se lee así: "haz lo que se especifica en el cuerpo del ciclo mientras la condición `i <= last_number` sea verdadera". +Así continúa hasta que `counter` sea igual a `5`. En la comprobación siguiente, la condición `counter < 5` será falsa, por eso el bucle terminará. Después el programa ejecutará el código que está tras el bucle. -Analicemos cómo funciona este código para la llamada `print_numbers(3)`: +La misma secuencia en un esquema. -```python -# Se inicializa i -i = 1 - -# El predicado es verdadero, por lo que se ejecuta el cuerpo del ciclo -while 1 <= 3 -# print(1) -# i = 1 + 1 - -# Se ha completado el cuerpo del ciclo, por lo que se vuelve al principio -while 2 <= 3 -# print(2) -# i = 2 + 1 - -# Se ha completado el cuerpo del ciclo, por lo que se vuelve al principio -while 3 <= 3 -# print(3) -# i = 3 + 1 - -# El predicado es falso, por lo que la ejecución pasa después del ciclo -while 4 <= 3 - -# print('¡finished!'); -# En este punto, i es igual a 4, pero ya no lo necesitamos -# La función finaliza +```text +counter = 0 +┌──→ ¿counter < 5? +│ True │ +│ ↓ +│ print("Hello!") +│ counter = counter + 1 +└──────────┘ + False → salida del bucle ``` -El proceso que genera el ciclo debe detenerse. Eso es responsabilidad del programador. +Después de terminar el bucle, `counter` es igual a `5` y la cadena `Hello!` se ha impreso cinco veces. -Por lo general, la tarea se reduce a introducir una variable: el **contador del ciclo**. Primero se inicializa, es decir, se le asigna un valor inicial. En nuestro ejemplo, esto se hace con la línea `i = 1`. Luego, en la condición del ciclo, se verifica si el contador ha alcanzado su valor límite. +## Sangrías y continuación del programa -El valor límite en el ejemplo se determina mediante el argumento de la función. Si la condición del ciclo no se cumple, el cuerpo no se ejecuta y el intérprete pasa a trabajar con las instrucciones después del ciclo. +Al cuerpo del bucle pertenecen todas las líneas con la misma sangría bajo el `while`. Cuando la sangría termina, termina también el bucle. -Si la condición del ciclo es verdadera, se ejecuta el cuerpo, que contiene el elemento de parada: el cambio del contador. Por lo general, esto se hace al final del cuerpo y es el lugar donde no se puede prescindir de una variable. En el ejemplo anterior, el cambio se realiza con la línea `i = i + 1`. +```python +counter = 0 +while counter < 2: + print("Hello!") + counter = counter + 1 + +print("End of loop") +``` -En este punto, los principiantes cometen muchos errores. Por ejemplo, se puede olvidar de incrementar el contador o verificarlo incorrectamente en el predicado. Esto conducirá a un bucle infinito, donde el ciclo se ejecutará indefinidamente y el programa nunca se detendrá. En ese caso, es necesario terminarlo forzosamente. +En este ejemplo, `print("Hello!")` y `counter = counter + 1` están dentro del bucle. La línea `print("End of loop")` está sin sangría, por eso se ejecutará una sola vez, después de terminar el bucle. + +Por las sangrías Python entiende qué líneas hay que repetir y cuáles van más adelante en el programa. + +## Un bucle dentro de una función + +Ahora trasladamos el bucle a una función. Imprimirá los números desde `1` hasta el valor recibido. ```python -def print_numbers(last_number): +def print_numbers(n: int) -> None: i = 1 - # Este ciclo nunca se detendrá - # y siempre imprimirá el mismo valor - while i <= last_number: + while i <= n: print(i) - print('finished!') -``` - -En algunos casos, los bucles infinitos son útiles. No vamos a considerar tales situaciones, pero mostraremos cómo se ve este código: + i = i + 1 + print("Finished!") -```python -while True: - # Hacer algo +print_numbers(3) +# => 1 +# => 2 +# => 3 +# => Finished! ``` -Los ciclos son indispensables cuando el algoritmo para resolver un problema requiere repetir ciertas acciones y la cantidad de estas operaciones no se conoce de antemano. +El bucle `while` imprime los números hasta que `i` sea mayor que `n`. Después de eso el programa sale del bucle y ejecuta `print("Finished!")`. + +La condición y la modificación del contador dependen de la tarea. El contador se puede aumentar en `1`, en `2` o de golpe en `10`. Se puede disminuir si el bucle va de un valor mayor a uno menor. El contador se puede cambiar no en cada repetición, sino cada dos o después de hacer una comprobación adicional. Lo principal es que la condición se vuelva falsa en algún momento. De lo contrario, el bucle funcionará infinitamente. diff --git a/modules/50-loops/10-while/es/assets/while.png b/modules/50-loops/10-while/es/assets/while.png new file mode 100644 index 00000000..0d10f271 Binary files /dev/null and b/modules/50-loops/10-while/es/assets/while.png differ diff --git a/modules/50-loops/15-conditions-inside-loops/es/README.md b/modules/50-loops/15-conditions-inside-loops/es/README.md index 09f60a5f..c5f2cceb 100644 --- a/modules/50-loops/15-conditions-inside-loops/es/README.md +++ b/modules/50-loops/15-conditions-inside-loops/es/README.md @@ -1,32 +1,51 @@ +Dentro de un bucle se pueden usar condiciones. Así el programa repite una acción varias veces, pero en cada repetición toma una decisión. -Dentro de un bucle, al igual que dentro de una función, se pueden ejecutar instrucciones. Por lo tanto, dentro de un bucle se puede utilizar todo lo aprendido anteriormente, como por ejemplo, estructuras condicionales. +![Condiciones dentro del bucle](./assets/conditions-inside-loops.png) -Imagina una función que cuenta cuántas veces aparece una letra en una oración. Aquí tienes un ejemplo de cómo funcion un bucle de esta forma: +Supongamos que hay que recorrer los números del `1` al `10` e imprimir solo los pares. El bucle recorre todos los números uno tras otro, y la condición dentro del bucle decide cuáles de ellos aparecerán en la pantalla. + +Para el recorrido hace falta un contador. Guarda el número actual y aumenta después de cada repetición. Hay que imprimir el número solo cuando pasa la comprobación. ```python -count_chars('El miedo corta más profundo que las espadas.', 'e') # 4 -# Si no se encuentra ninguna coincidencia, el resultado es 0 -count_chars('Sansa', 'y') # 0 +number = 1 +while number <= 10: + if number % 2 == 0: + print(number) + number = number + 1 + +# => 2 +# => 4 +# => 6 +# => 8 +# => 10 ``` -Antes de ver el contenido de la función, piensa en lo siguiente: +El bucle `while` recorre los números del `1` al `10`. La condición dentro del bucle comprueba el número actual. Si `number % 2 == 0`, el número se divide entre `2` sin resto y el programa lo muestra en la pantalla. + +El contador aumenta después de la comprobación en cualquier caso. Eso es importante. Si se aumentara `number` solo dentro del `if`, el bucle se detendría en el primer número impar y funcionaría infinitamente. + +## El funcionamiento paso a paso + +Antes de la primera repetición, `number` es igual a `1`. + +**Paso 1.** La condición del bucle `number <= 10` es verdadera, por eso el programa entra en el cuerpo del bucle. El número `1` es impar, el bloque `if` no se ejecuta. Después `number` aumenta hasta `2`. + +**Paso 2.** La condición del bucle es verdadera de nuevo. El número `2` es par, por eso el programa imprime `2`. Después `number` aumenta hasta `3`. + +Más adelante el bucle sigue comprobando cada número. Los números impares los omite y los pares los muestra en la pantalla. Cuando `number` sea igual a `11`, la condición `number <= 10` será falsa y el bucle terminará. + +## Las condiciones cambian la acción, no el movimiento -* ¿Es esta operación una agregación? -* ¿Cuál será la condición para verificar la aparición de un carácter? +En esos bucles resulta cómodo separar dos partes. El contador lleva el programa al valor siguiente, y el `if` decide qué hacer con el valor actual. ```python -def count_chars(string, char): - index = 0 - count = 0 - while index < len(string): - if string[index] == char: - # Solamente contamos los caracteres que coinciden - count = count + 1 - # El contador se incrementa en cualquier caso - index = index + 1 - return count +number = 1 +while number <= 10: + if number > 5: + print(number) + number = number + 1 ``` -Esta es una tarea de agregación. Aunque no cuenta todos los caracteres para calcular la suma, es necesario analizar cada carácter. La diferencia clave de este bucle con los que se han visto anteriormente es que contiene una condición en su cuerpo. +Aquí el bucle recorre el mismo rango del `1` al `10`, pero la condición de dentro es otra. Por eso el programa imprime solo los números mayores que `5`. -La variable `count` solo se incrementa cuando el carácter actual coincide con el carácter esperado. De lo contrario, esta función es típicamente una función agregada que devuelve la cantidad de caracteres necesarios. +La condición dentro del bucle puede comprobar cualquier cosa. Por ejemplo, la paridad del número, la coincidencia de un carácter, la longitud de una cadena o el valor de una variable. Lo principal es que el contador siga cambiando y que el bucle pueda terminar. diff --git a/modules/50-loops/15-conditions-inside-loops/es/assets/conditions-inside-loops.png b/modules/50-loops/15-conditions-inside-loops/es/assets/conditions-inside-loops.png new file mode 100644 index 00000000..e016c272 Binary files /dev/null and b/modules/50-loops/15-conditions-inside-loops/es/assets/conditions-inside-loops.png differ diff --git a/modules/50-loops/20-aggregation-numbers/es/README.md b/modules/50-loops/20-aggregation-numbers/es/README.md index 3ac6860d..04fbbde1 100644 --- a/modules/50-loops/20-aggregation-numbers/es/README.md +++ b/modules/50-loops/20-aggregation-numbers/es/README.md @@ -1,58 +1,65 @@ +Una clase aparte de tareas que no se resuelven sin bucles se llama **agregación de datos**. A esas tareas pertenecen la búsqueda del valor máximo o mínimo, de la suma o de la media aritmética. En su caso el resultado depende de todo el conjunto de datos. En esta lección analizaremos cómo se aplica la agregación a los números y a las cadenas. -Una clase de tareas que no se puede resolver sin bucles se llama **agregación de datos**. Estos problemas incluyen la búsqueda del valor máximo o mínimo, la suma y el promedio aritmético. En estos casos, el resultado depende de todo el conjunto de datos. +![Iteraciones del bucle](./assets/iterations.png) -Para calcular la suma, es necesario sumar todos los números, y para calcular el máximo, es necesario compararlos. Los contadores o los especialistas en marketing están familiarizados con este tipo de problemas. Trabajan con tablas en Microsoft Excel o Google Sheets. +Supongamos que necesitamos encontrar la suma de un conjunto de números. Implementemos una función que suma los números de un rango indicado, incluidos los límites. Un **rango** es una serie de números desde un principio concreto hasta un final determinado. Por ejemplo, el rango [1, 10] incluye los números enteros del uno al diez. -En esta lección, veremos cómo se aplica la agregación a los números y las cadenas de texto. - -Supongamos que necesitamos encontrar la suma de un conjunto de números. Implementaremos una función que sume los números en un rango especificado, incluyendo los límites. Un **rango** es una serie de números desde un punto de inicio hasta un punto final específicos. Por ejemplo, el rango [1, 10] incluye todos los números enteros del uno al diez. - -Ejemplo: +Un ejemplo: ```python sum_numbers_from_range(5, 7) # 5 + 6 + 7 = 18 sum_numbers_from_range(1, 2) # 1 + 2 = 3 -# [1, 1] es un rango con el mismo inicio y fin, también es un rango -# Incluye un solo número, que es el límite del rango +# [1, 1] un rango con el mismo principio y final también es un rango +# Incluye un solo número: el límite mismo del rango sum_numbers_from_range(1, 1) # 1 sum_numbers_from_range(100, 100) # 100 ``` -Para implementar este código, necesitaremos un bucle, ya que la suma de números es un proceso iterativo, es decir, se repite para cada número. El número de iteraciones depende del tamaño del rango. - -Echa un vistazo al siguiente código: +Para implementar ese código hará falta un bucle, ya que sumar números es un proceso iterativo, es decir, se repite para cada número. La cantidad de iteraciones depende del tamaño del rango. Este es el código de esa función: ```python -def sum_numbers_from_range(start, finish): - # Técnicamente, se puede cambiar el valor de inicio - # Pero los argumentos de entrada deben mantenerse en su valor original - # Esto hace que el código sea más fácil de analizar +def sum_numbers_from_range(start: int, finish: int) -> int: + # Técnicamente se puede modificar start + # Pero los argumentos de entrada hay que dejarlos en su valor original + # Eso hace el código más simple de analizar i = start sum = 0 # Inicialización de la suma - while i <= end: # Avanzamos hasta el final del rango - suma = sum + i # Calculamos la suma para cada número - i = i + 1 # Pasamos al siguiente número en el rango + while i <= finish: # Avanzamos hasta el final del rango + sum = sum + i # Calculamos la suma para cada número + i = i + 1 # Pasamos al número siguiente del rango # Devolvemos el resultado obtenido return sum ``` -La estructura del bucle es estándar: hay un contador que se inicializa con el valor inicial del rango, un bucle con una condición de finalización cuando se alcanza el final del rango y una modificación del contador al final del cuerpo del bucle. El número de iteraciones en este tipo de bucle es igual a `fin - inicio + 1`. Para el rango [5, 7], esto es 7 - 5 + 1, es decir, tres iteraciones. +La estructura del bucle aquí es la estándar. Hay un contador que se inicializa con el valor inicial del rango, un bucle con la condición de parada al alcanzar el final del rango y la modificación del contador al final del cuerpo del bucle. La cantidad de iteraciones en ese bucle es igual a `finish - start + 1`. Para el rango [5, 7] eso es 7 - 5 + 1, es decir, tres iteraciones. -Las principales diferencias con el procesamiento normal son la lógica de cálculo del resultado. En los problemas de agregación, siempre hay una variable que almacena el resultado del bucle. En el código anterior, esta variable es `suma`. Se modifica en cada iteración del bucle, sumando el siguiente número del rango: `suma = suma + i`. +La diferencia principal con el procesamiento normal está en la lógica del cálculo del resultado. En las tareas de agregación siempre hay una variable que guarda dentro de sí el resultado del trabajo del bucle. En el código de arriba es `sum`. En cada iteración del bucle se le añade el número siguiente del rango: `sum = sum + i`. -Este proceso se ve así: +De forma visual, ese proceso se ve de la siguiente manera. ```python -# para la llamada y ejecución sum_numbers_from_range(2, 5) +# Para la llamada sum_numbers_from_range(2, 5) sum = 0 sum = sum + 2 # 2 sum = sum + 3 # 5 sum = sum + 4 # 9 sum = sum + 5 # 14 -# 14 es el resultado de sumar los números en el rango [2, 5] +# 14 es el resultado de sumar los números del rango [2, 5] +``` + +De forma visual, el proceso de acumulación de la suma se ve así. + +```text +sum_numbers_from_range(2, 5): + +i=2: sum = 0 + 2 = 2 +i=3: sum = 2 + 3 = 5 +i=4: sum = 5 + 4 = 9 +i=5: sum = 9 + 5 = 14 + └── resultado ``` -La variable `sum` tiene un valor inicial, desde el cual comienza cualquier operación repetitiva. En el ejemplo anterior, ese valor es `0`. +La variable `sum` tiene un valor inicial con el que empieza cualquier operación repetida. En el ejemplo de arriba es `0`. ¿Por qué así? -En matemáticas, hay un concepto de **elemento neutro** y cada operación tiene el suyo. La operación con este elemento no cambia el valor con el que se está trabajando. Por ejemplo, en la suma, cualquier número más cero es igual al número original. Lo mismo ocurre con la resta. La concatenación también tiene un elemento neutro, que es la cadena vacía: `'' + 'one'` será 'one'. +En matemáticas existe el concepto de **elemento neutro**, y cada operación tiene el suyo. La operación con ese elemento no modifica el valor sobre el que trabaja. Por ejemplo, en la suma cualquier número más cero da el número mismo. En la resta el elemento neutro es el mismo: 0. En la concatenación el elemento neutro es la cadena vacía: `'' + 'one'` será 'one'. Por eso, si multiplicáramos, en lugar de `0` usaríamos `1`. diff --git a/modules/50-loops/20-aggregation-numbers/es/assets/iterations.png b/modules/50-loops/20-aggregation-numbers/es/assets/iterations.png new file mode 100644 index 00000000..c768d5a6 Binary files /dev/null and b/modules/50-loops/20-aggregation-numbers/es/assets/iterations.png differ diff --git a/modules/50-loops/23-aggregation-strings/es/EXERCISE.md b/modules/50-loops/23-aggregation-strings/es/EXERCISE.md index e0b03426..045e6475 100644 --- a/modules/50-loops/23-aggregation-strings/es/EXERCISE.md +++ b/modules/50-loops/23-aggregation-strings/es/EXERCISE.md @@ -1,10 +1,14 @@ - Implementa la función `sanitize_phone_number()`, que recibe un número de teléfono de un formulario y devuelve una cadena sin espacios, paréntesis ni guiones. -Los usuarios escriben los números de teléfono de distintas maneras, pero las aplicaciones suelen guardarlos en un solo formato. Recorre la cadena original carácter por carácter y construye un nuevo número solo con los caracteres útiles. +Los usuarios escriben los números de maneras distintas, pero antes de guardarlos se los suele llevar a un formato único. Recorre la cadena original carácter por carácter y construye un número nuevo solo con los caracteres útiles. ```python sanitize_phone_number('+7 (999) 123-45-67') # '+79991234567' sanitize_phone_number('8 800 555 35 35') # '88005553535' sanitize_phone_number('(123) 456-7890') # '1234567890' ``` + +### Pistas + +* El resultado final es una cadena. +* Usa la cadena vacía como valor inicial. diff --git a/modules/50-loops/23-aggregation-strings/es/README.md b/modules/50-loops/23-aggregation-strings/es/README.md index 0fc976df..e7498025 100644 --- a/modules/50-loops/23-aggregation-strings/es/README.md +++ b/modules/50-loops/23-aggregation-strings/es/README.md @@ -1,34 +1,57 @@ +La agregación de cadenas reúne las tareas en las que no se sabe de antemano qué contienen las cadenas ni cuál es su tamaño. -La agregación de cadenas es cuando no se sabe de antemano qué contienen las cadenas y cuál es su tamaño. - -Imagina una función que pueda multiplicar una cadena, repitiéndola un número fijo o determinado de veces: +Imaginemos una función que repite una cadena la cantidad indicada de veces. Sí, en Python está incorporado para la repetición el operador de multiplicar una cadena por un número, pero aquí veremos cómo está implementado aproximadamente por dentro: ```python repeat('hexlet', 3) # 'hexlethexlethexlet' ``` -El principio de funcionamiento de esta función es que en un bucle se va "acumulando" la cadena un número determinado de veces: +La función va aumentando la cadena en un bucle la cantidad indicada de veces: ```python -def repeat(text, times): - # El elemento neutro para las cadenas es una cadena vacía +def repeat(text: str, times: int) -> str: + # El elemento neutro de las cadenas es la cadena vacía result = '' i = 1 while i <= times: - # En cada iteración, se agrega la cadena al resultado + # Cada vez añadimos la cadena al resultado result = result + text i = i + 1 return result ``` -Veamos cómo se ejecuta este código paso a paso: +Describamos la ejecución de ese código paso a paso. ```python -# Para llamar a repeat('hexlet', 3) +# Para la llamada repeat('hexlet', 3) result = '' result = result + 'hexlet' # hexlet result = result + 'hexlet' # hexlethexlet result = result + 'hexlet' # hexlethexlethexlet ``` + +De forma visual, el proceso de crecimiento de la cadena se ve así. + +```text +repeat('hexlet', 3): + +i=1: result = '' + 'hexlet' = 'hexlet' +i=2: result = 'hexlet' + 'hexlet' = 'hexlethexlet' +i=3: result = 'hexlethexlet' + 'hexlet' = 'hexlethexlethexlet' + └── resultado +``` + +## El elemento neutro + +Para que el crecimiento funcione hace falta un valor de partida. Para las cadenas, ese valor es la **cadena vacía** `''`. + +Se llama elemento neutro porque en la concatenación no cambia nada: + +```python +print('' + 'abc') # => abc +print('abc' + '') # => abc +``` + +Por eso es precisamente la cadena vacía la que se usa siempre como valor inicial en la agregación de cadenas. diff --git a/modules/50-loops/25-iteration-over-string/es/EXERCISE.md b/modules/50-loops/25-iteration-over-string/es/EXERCISE.md index 0ddd7094..f06e4438 100644 --- a/modules/50-loops/25-iteration-over-string/es/EXERCISE.md +++ b/modules/50-loops/25-iteration-over-string/es/EXERCISE.md @@ -1,5 +1,6 @@ +Escribe la función `mask_card_number()`, que oculta el número de una tarjeta bancaria. La función debe reemplazar todos los caracteres de la cadena por `*`, excepto los cuatro últimos. -Escribe la función `mask_card_number()`, que oculta el número de una tarjeta bancaria. La función debe reemplazar todos los caracteres de la cadena por `*`, excepto los últimos cuatro. +Un ejemplo de funcionamiento: ```python mask_card_number("1234567812345678") # "************5678" diff --git a/modules/50-loops/25-iteration-over-string/es/README.md b/modules/50-loops/25-iteration-over-string/es/README.md index 4411b836..92d268a4 100644 --- a/modules/50-loops/25-iteration-over-string/es/README.md +++ b/modules/50-loops/25-iteration-over-string/es/README.md @@ -1,13 +1,12 @@ +Con los bucles se procesan números y se trabaja con cadenas. Por ejemplo, se puede obtener un carácter concreto por su índice y también formar cadenas en bucles. -Con la ayuda de los bucles, no sólo se pueden procesar números, sino también cadenas de texto. Por ejemplo, se puede obtener un carácter específico por su índice, así como formar cadenas de texto en bucles. - -A continuación se muestra un ejemplo de código que imprime las letras de cada palabra en una línea separada: +Abajo hay un ejemplo de código que imprime las letras de cada palabra en una línea aparte: ```python -def print_name_by_symbol(name): +def print_name_by_symbol(name: str) -> None: i = 0 - # Esta condición se cumplirá hasta el final de la cadena, - # incluyendo el último carácter. Su índice es `length - 1`. + # Esa comprobación se ejecutará hasta el final de la cadena, + # incluido el último carácter. Su índice es `length - 1`. while i < len(name): # Accedemos al carácter por su índice print(name[i]) @@ -21,4 +20,33 @@ print_name_by_symbol(name) # => 'a' ``` -Lo más importante en este código es establecer la condición correcta en `while`. Esto se puede hacer de dos formas: `i < len(name)` o `i <= len(name) - 1` - ambas conducirán al mismo resultado. +El bucle recorre cada carácter de la cadena por turnos: + +```text +'Arya' + │ │ │ │ + A r y a + ↓ ↓ ↓ ↓ +cada carácter se procesa por turnos +``` + +En este código es importante poner correctamente la condición del `while`. Las dos variantes, `i < len(name)` e `i <= len(name) - 1`, llevarán al mismo resultado. + +## Invertir una cadena + +En lugar de imprimir se puede construir una cadena nueva. Por ejemplo, escribamos una función que invierte una cadena: + +```python +def reverse_string(text: str) -> str: + result = '' + i = len(text) - 1 + while i >= 0: + result = result + text[i] + i = i - 1 + return result + +print(reverse_string('Arya')) # => ayrA +print(reverse_string('hexlet')) # => telxeh +``` + +La variable `result` se inicializa con la cadena vacía como elemento neutro de la concatenación. El bucle empieza en el último índice (`len(text) - 1`), avanza hacia cero y termina cuando el índice se vuelve menor que cero. En cada paso se añade al resultado el carácter actual. Al final la cadena se construye en orden inverso. diff --git a/modules/50-loops/30-syntactic-sugar/es/EXERCISE.md b/modules/50-loops/30-syntactic-sugar/es/EXERCISE.md index b3f45c57..f92b873b 100644 --- a/modules/50-loops/30-syntactic-sugar/es/EXERCISE.md +++ b/modules/50-loops/30-syntactic-sugar/es/EXERCISE.md @@ -1,10 +1,13 @@ +Los operadores de azúcar sintáctico resultan especialmente cómodos cuando hay que armar poco a poco el valor final. -Implementa la función `build_progress_bar()`, que recibe la cantidad de pasos completados y la cantidad total de pasos, y devuelve una cadena que representa una barra de progreso. +Implementa la función `build_progress_bar()`, que recibe la cantidad de pasos completados y la cantidad total de pasos, y después devuelve una cadena-indicador del progreso. -Los pasos completados se muestran con `#`, y los pasos restantes se muestran con `-`. Intenta no utilizar métodos incorporados para trabajar con cadenas en tu solución. +Los pasos completados se marcan con el carácter `#` y los restantes con el carácter `-`. Procura no usar métodos incorporados de trabajo con cadenas en tu solución. ```python build_progress_bar(0, 5) # '-----' build_progress_bar(3, 5) # '###--' build_progress_bar(5, 5) # '#####' ``` + +Te será útil el operador `+=` para armar poco a poco la cadena nueva dentro del bucle `while`. Y también te servirá para gobernar la condición del bucle. diff --git a/modules/50-loops/30-syntactic-sugar/es/README.md b/modules/50-loops/30-syntactic-sugar/es/README.md index 35fb5412..e8b7dda8 100644 --- a/modules/50-loops/30-syntactic-sugar/es/README.md +++ b/modules/50-loops/30-syntactic-sugar/es/README.md @@ -1,24 +1,39 @@ +En programación aparecen a menudo construcciones repetidas. En Python, como en muchos otros lenguajes, existe la posibilidad de acortar su escritura. Esas simplificaciones se llaman **azúcar sintáctico**. Hacen el proceso de escribir código más corto y más cómodo, conservando el mismo resultado. -Construcciones como `index = index + 1` se utilizan frecuentemente en Python, por lo que los creadores del lenguaje agregaron una forma abreviada: `index += 1`. +## Formas abreviadas de asignación -Solamente difieren en la forma de escribirlos. El intérprete convertirá la construcción abreviada en la expandida. +A menudo hace falta modificar el valor de una variable, sumándole o restándole algo, multiplicándolo o dividiéndolo por un número. La variante básica se ve así: -Estas abreviaturas se llaman **azúcar sintáctico**, porque hacen que el proceso de escribir código sea un poco más fácil y agradable. +```python +index = index + 1 +count = count * 2 +total = total - 5 +price = price / 3 +``` + +Python permite escribirlo de forma más corta, con los operadores combinados: + +```python +index += 1 # lo mismo que index = index + 1 +count *= 2 # lo mismo que count = count * 2 +total -= 5 # lo mismo que total = total - 5 +price /= 3 # lo mismo que price = price / 3 +``` -Esta forma aparece especialmente a menudo en los ciclos, donde normalmente cambiamos un contador y acumulamos un resultado: +En los bucles esas abreviaturas aparecen con especial frecuencia. En ellos normalmente cambiamos el contador y acumulamos el resultado: ```python sum = 0 index = 1 while index <= 5: - sum += index # Lo mismo que sum = sum + index - index += 1 # Lo mismo que index = index + 1 + sum += index # lo mismo que sum = sum + index + index += 1 # lo mismo que index = index + 1 print(sum) # => 15 ``` -Sin la forma abreviada, el cuerpo del ciclo sería más largo: +Sin las abreviaturas, el cuerpo del bucle sería más largo: ```python while index <= 5: @@ -26,9 +41,17 @@ while index <= 5: index = index + 1 ``` -Existen formas abreviadas para todas las operaciones aritméticas y para la concatenación de cadenas: +## Otras operaciones + +Esa forma de escribir funciona con números y con otros tipos de datos. + +Para las cadenas se usa el operador de concatenación: + +```python +text = "Hello" +text += " World" # lo mismo que text = text + " World" +``` + +## Abreviaturas admitidas -* `a = a + 1` → `a += 1` -* `a = a - 1` → `a -= 1` -* `a = a * 2` → `a *= 2` -* `a = a / 1` → `a /= 1` +Existe una forma abreviada para casi todos los operadores: `+=`, `-=`, `*=`, `/=`, `//=`, `%=`, `**=`. Todas funcionan según el mismo principio: toman el valor actual de la variable, aplican la operación y guardan el resultado en la misma variable. diff --git a/modules/50-loops/55-return-from-loops/es/README.md b/modules/50-loops/55-return-from-loops/es/README.md index 5105f628..0eb24b4e 100644 --- a/modules/50-loops/55-return-from-loops/es/README.md +++ b/modules/50-loops/55-return-from-loops/es/README.md @@ -1,15 +1,20 @@ +El trabajo con bucles se reduce normalmente a dos escenarios. En el primero, el resultado se acumula durante las iteraciones y el trabajo con él ocurre ya después del bucle. Ese enfoque se llama agregación. Invertir una cadena pertenece a esa variante. En el segundo escenario, el bucle se ejecuta hasta alcanzar el resultado necesario y termina de forma anticipada. Así se implementa, por ejemplo, la tarea de buscar números primos, que se dividen sin resto solo entre sí mismos y entre uno. -Trabajar con bucles generalmente se reduce a dos escenarios: +Veamos el algoritmo de comprobación de la primalidad de un número. Dividiremos el número buscado `x` entre todos los números del rango del dos hasta `x - 1` y miraremos el resto. Si en ese rango no se encuentra un divisor que divida el número `x` sin resto, entonces ante nosotros hay un número primo. -1. Agregación. Acumular resultados durante las iteraciones y trabajar con ellos después del bucle. Voltear una cadena es un ejemplo de este caso. -2. Ejecutar el bucle hasta que se alcance un resultado deseado y luego salir. Por ejemplo, la tarea de encontrar números primos, que solo son divisibles exactamente por sí mismos y por uno. +## Comprobación de la primalidad del número 5: análisis paso a paso -Veamos el algoritmo para comprobar si un número es primo. Dividiremos el número buscado `x` entre todos los números en el rango desde dos hasta `x - 1` y observaremos el residuo. Si no encontramos ningún divisor en este rango que divida al número `x` sin residuo, entonces estamos ante un número primo. +1. Tomamos el número x = 5. Los divisores posibles los buscamos en el rango del 2 hasta x - 1, es decir, del 2 al 4. +2. Dividimos 5 entre 2. El resto es igual a 1, no encontramos divisor, continuamos. +3. Dividimos 5 entre 3. El resto es igual a 2, no encontramos divisor, continuamos. +4. Dividimos 5 entre 4. El resto es igual a 1, no encontramos divisor, terminamos el recorrido. -En este caso, solo es necesario verificar los números hasta `x / 2`, en lugar de hasta `x - 1`. Por ejemplo, 11 no es divisible por 2, 3, 4, 5. Pero tampoco será divisible por números mayores que la mitad de sí mismo. Por lo tanto, podemos optimizar el algoritmo y verificar la división solo hasta `x / 2`: +Resultado. En el rango 2…4 no se encontró ni un número entre el que 5 se divida sin resto. Por consiguiente, 5 es un número primo. + +En este caso basta con limitar la búsqueda de divisores hasta la mitad del número. Por ejemplo, 11 no se divide entre 2, 3, 4, 5, y entre los números mayores que su mitad tampoco se dividirá. Eso significa que se puede optimizar el algoritmo y comprobar la división solo hasta `x / 2`: ```python -def is_prime(number): +def is_prime(number: int) -> bool: if number < 2: return False @@ -29,8 +34,17 @@ print(is_prime(3)) # => True print(is_prime(4)) # => False ``` -*Siendo completamente honestos, para resolver el problema es suficiente verificar los números hasta la raíz cuadrada de `numero`, pero en nuestro caso es importante concentrarse en comprender las condiciones dentro del bucle.* +*Si somos honestos hasta el final, para resolver la tarea basta con comprobar los números hasta el valor de la raíz cuadrada de `number`, pero en nuestro caso es importante centrarse en entender el trabajo con condiciones dentro de un bucle* + +```text +while ...: + if condición: + return valor ← salida de la función (y del bucle) + ... +───────────────────────── +Sin return el bucle continúa hasta el final +``` -Imaginemos que en el algoritmo de división secuencial de números hasta `x / 2` encontramos uno que divide sin residuo. Entonces, el argumento proporcionado no es un número primo y no tiene sentido continuar con los cálculos. En este punto, deberíamos retornar `False`. +Imaginemos que, según el algoritmo de división sucesiva entre los números hasta `x / 2`, se encontró uno que divide sin resto. Eso significa que el argumento recibido es un número compuesto y que los cálculos posteriores no tienen sentido. En ese punto está el retorno de `False`. -Si el bucle se ejecuta por completo y no encontramos ningún número que divida sin residuo, entonces el número es primo. +Si el bucle se ejecutó por completo y no se encontró un número que divida sin resto, entonces el número es primo. diff --git a/modules/50-loops/70-for/es/README.md b/modules/50-loops/70-for/es/README.md index f4ca8fb4..5fc1df1f 100644 --- a/modules/50-loops/70-for/es/README.md +++ b/modules/50-loops/70-for/es/README.md @@ -1,11 +1,8 @@ +Con el bucle `while` se resuelve cualquier tarea de recorrido de elementos, pero lo caracteriza su verbosidad. Para el `while` hay que fijar la condición de parada e introducir un contador. Cuando hay pocos bucles eso es normal, pero en el código real los bucles aparecen a cada paso. Por eso gestionar las condiciones a mano resulta fatigoso, sobre todo cuando la condición de parada es evidente. -El ciclo `while` se utiliza para resolver cualquier problema de iteración, pero es bastante extenso y verboso. Con `while`, es necesario establecer una condición de finalización y un contador. Cuando hay pocos ciclos, esto no es un problema, pero en el código real, los ciclos están presentes en cada paso. Por lo tanto, controlar las condiciones manualmente puede resultar tedioso, especialmente cuando la condición de finalización es obvia. +Por ejemplo, si queremos recorrer los caracteres de una cadena, el ordenador puede entender por sí mismo cuándo termina la cadena. Para esas situaciones, en Python existe el bucle `for`. Él mismo sabe cuándo hay que detenerse: `for` trabaja con valores que se pueden recorrer elemento por elemento (por ejemplo, con una cadena). -Por ejemplo, si queremos iterar sobre los caracteres de una cadena, la computadora puede entender automáticamente cuándo la cadena termina. Para este tipo de situaciones, en Python se introdujo el ciclo `for`. Este ciclo sabe automáticamente cuándo detenerse, ya que solo trabaja con colecciones, es decir, conjuntos de elementos que deben ser iterados. - -Una cadena es una colección, ya que está compuesta por un conjunto de caracteres. Otros tipos de colecciones se estudian en otro curso. - -Ejemplo: +Una cadena consta de caracteres, y el bucle `for` permite recorrer la cadena carácter a carácter. Un ejemplo: ```python text = 'code' @@ -18,41 +15,68 @@ for symbol in text: # => e ``` -En el código anterior, el ciclo `for` recorre cada carácter de la cadena, lo guarda en la variable `símbolo` y ejecuta el bloque de código interno donde se utiliza esta variable. El nombre de esta variable puede ser cualquier cosa. La estructura general del ciclo `for` es: `for in `. +En el código de arriba, `for` recorre cada carácter de la cadena, lo escribe en la variable `symbol` y llama al bloque interno de código, donde se usa esa variable. El nombre de esa variable puede ser cualquiera. La estructura general del bucle `for` se ve así: `for in `. + +Comparemos `for` y `while` para una misma tarea: -Veamos cómo implementar una función para invertir una cadena utilizando el ciclo `for`: +```text +for char in 'Hi!': while i < len('Hi!'): + print(char) print(text[i]) + i = i + 1 +│ │ +└── más simple └── hace falta un contador +``` + +Veamos cómo implementar la función de invertir una cadena con el bucle `for`. Esa tarea aparece en las entrevistas. ```python -def reverse_string(text): - # Valor inicial - result = '' - # char - variable donde se guarda el carácter actual - for char in text: - # Concatenamos en orden inverso - result = char + result - # El ciclo termina cuando se ha recorrido toda la cadena - print(result) +def reverse_string(text: str) -> str: + # Valor inicial + result = '' + # char - variable en la que se escribe el carácter actual + for char in text: + # Unimos en orden inverso + result = char + result + # El bucle termina cuando se ha recorrido toda la cadena + return result reverse_string('go!') # => '!og' ``` -Analicemos detalladamente cómo funciona el ciclo en el ejemplo anterior en cada iteración: +Analicemos en detalle cómo se ejecuta el bucle del ejemplo de arriba en cada paso de las iteraciones. -1. En la primera iteración, en el ciclo `result` hay una cadena vacía y `char` es igual al primer carácter '¡'. Por lo tanto, en `resultado` se asigna el carácter '¡' más la cadena vacía, lo que resulta en '¡'. -2. En la segunda iteración, `result` ya contiene el carácter '¡' y `char` es igual al siguiente carácter 'v'. En `result` se guarda `char + result`, es decir, `'v' + '¡'`. -3. En la última iteración, `result` contiene la cadena '¡v' y `char` es igual al último carácter '!'. En `result` se guarda `'¡v' + '!'`. +```text +reverse_string('go!') -Ahora contemos la cantidad de veces que aparece un carácter en una cadena sin tener en cuenta el caso: +antes del bucle: result = '' + +iteración 1: char = 'g' +result = char + result + = 'g' + '' + = 'g' + +iteración 2: char = 'o' +result = char + result + = 'o' + 'g' + = 'og' + +iteración 3: char = '!' +result = char + result + = '!' + 'og' + = '!og' +``` + +Ahora contemos la cantidad de apariciones de un carácter en una cadena sin tener en cuenta el caso: ```python # text - texto arbitrario -# char - carácter que se debe contar -def chars_count(text, char): - # Como estamos buscando la suma, el valor inicial es 0 +# char - carácter que hay que contar +def chars_count(text: str, char: str) -> int: + # Como buscamos una suma, el valor inicial es 0 result = 0 for current_char in text: - # convertimos todo a minúsculas + # pasamos todo a minúsculas, # para no depender del caso actual if current_char.lower() == char.lower(): result += 1 @@ -62,6 +86,5 @@ def chars_count(text, char): chars_count('hexlet!', 'e') # 2 chars_count('hExlet!', 'e') # 2 chars_count('hExlet!', 'E') # 2 - chars_count('hexlet!', 'a') # 0 ``` diff --git a/modules/50-loops/80-for-in-range/es/README.md b/modules/50-loops/80-for-in-range/es/README.md new file mode 100644 index 00000000..36acca9d --- /dev/null +++ b/modules/50-loops/80-for-in-range/es/README.md @@ -0,0 +1,61 @@ +Imagina que tenemos una serie de números del 0 al 9. Queremos sumar esos números. Podríamos hacerlo así: + +```python +sum = 0 +i = 0 + +while i < 10: + sum += i + i += 1 + +print(sum) # => 45 +``` + +Ese código lo podemos reescribir con el bucle `for` + `range()` + +```python +sum = 0 + +for i in range(10): + sum += i + +print(sum) # => 45 +``` + +La función range en Python crea una secuencia de números dentro de un rango determinado. Se puede usar en el bucle for para controlar la cantidad de iteraciones. + +Admite varias variantes de llamada. La forma `range(stop)` crea una secuencia desde 0 hasta `stop - 1`. La forma `range(start, stop)` crea una secuencia desde start hasta `stop - 1`. La forma `range(start, stop, step)` crea una secuencia de números desde start hasta `stop - 1` con el paso `step`. + +De forma visual, las variantes de `range()` se ven así. + +```text +range(1, 5) → 1, 2, 3, 4 +range(0, 3) → 0, 1, 2 +range(5) → 0, 1, 2, 3, 4 + └── empieza en 0, el final no se incluye +``` + +El ejemplo con un solo valor final lo vimos arriba. Veamos otro: imprimamos en pantalla los números del 1 al 3: + +```python + +for i in range(1, 4): + print(i) + +# => 1 +# => 2 +# => 3 +``` + +Ahora intentemos mostrar los números en orden inverso + +```python +for i in range(3, 0, -1): + print(i) + +# => 3 +# => 2 +# => 1 +``` + +En los ejemplos de arriba vemos que la iteración termina antes del valor final. diff --git a/modules/50-loops/90-debug/es/README.md b/modules/50-loops/90-debug/es/README.md new file mode 100644 index 00000000..c914bb9f --- /dev/null +++ b/modules/50-loops/90-debug/es/README.md @@ -0,0 +1,119 @@ +Incluso a los desarrolladores más experimentados el código rara vez les funciona a la perfección la primera vez. Cuanto más experimentado es el desarrollador, con más seguridad lo **depura**, es decir, analiza los errores y los elimina. + +La habilidad de depurar no aparece por sí sola. Hay que desarrollarla, y además lo antes posible. A lo largo del aprendizaje harás ejercicios y practicarás, y con el tiempo el análisis de los errores se convertirá en un hábito. + +## Cómo encontrar el error en el código + +Depurar a base de prueba y error lleva mucho tiempo. Es mucho más productivo entender primero qué fue exactamente lo que salió mal y luego eliminar la causa. Cualquier trabajo con errores empieza por la comprensión. + +Lo primero es estudiar el **traceback**. El traceback contiene la lista de todas las llamadas a funciones desde el arranque del programa hasta el lugar del error. Por él se ve qué funciones se ejecutaron con éxito y dónde surgieron los problemas. Cada registro indica el archivo, la línea y la función que se estaba ejecutando. + +Imaginemos que escribiste código en el archivo `users.py` y llamaste a la función `main()` en la cuarta línea. El registro en el traceback se verá así: + +```bash +File "users.py", line 4, in + main() +``` + +Aquí se ve no solo el archivo y la línea, sino también el nombre del módulo. Por él se puede determinar si el problema surgió en tu código o en una biblioteca de terceros. + +Cuando el traceback llega al lugar problemático, muestra un **mensaje de error**. Por ejemplo: + +```bash +NameError: name 'create' is not defined +``` + +El mensaje dice que el nombre `create` no está definido. Ese error significa, la mayoría de las veces, un error tipográfico en el nombre. Si tu inglés todavía no es muy bueno, te ayudará un traductor. + +Juntos, el traceback y el mensaje de error se ven así: + +```bash +Traceback (most recent call last): + File "users.py", line 4, in + main() + File "users.py", line 2, in main + create() +NameError: name 'create' is not defined +``` + +Toda la cadena de sucesos se ve de golpe. El programa llegó con éxito hasta `main()`, después pasó a `create()` y aquí se topó con un error en el nombre. + +## Tipos de errores + +Los errores más comprensibles en Python se llaman **sintácticos**. Surgen cuando el código está escrito incorrectamente, por ejemplo por una comilla equivocada o un paréntesis omitido. En la salida siempre está presente `SyntaxError:`. + +Miremos un ejemplo. Aquí hay un error sintáctico debido a que la comilla de apertura `"` no coincide con la de cierre `'`: + +```bash +Traceback (most recent call last): + File "users.py", line 2 + print("Hello" + "world') + ^ +SyntaxError: EOL while scanning string literal +``` + +Lo más difícil de corregir son los **errores de programación**. Aquí entran la llamada a una función que no existe, el uso de una variable no declarada y el paso de argumentos de tipo incorrecto. Normalmente surgen no en el lugar donde está la causa real, lo que complica el diagnóstico. + +Con lo más difícil de luchar es con los **errores lógicos**. El programa funciona sin excepciones, pero da un resultado incorrecto con algunos datos de entrada. No hay ningún mensaje de error, solo una salida inesperada. Por ejemplo, la función debe calcular la suma, pero calcula la diferencia: + +```python +# La función debe calcular la suma de los números, pero calcula la diferencia: +def sum(a: int, b: int) -> int: + return a - b + + +# Con esa llamada el error no es evidente, porque +# tanto con la suma como con la resta el resultado será el mismo +sum(4, 0) # 4 +``` + +## Formas de depurar + +En la base de cualquier método de depuración está la observación de las variables durante la ejecución. Miremos una función concreta. + +Abajo hay una función que calcula la suma de los números desde `start` hasta `finish`. Con `start=3` y `finish=5` debe calcular `3 + 4 + 5`. + +```python +def sum_of_series(start: int, finish: int) -> int: + result = 0 + n = start + while n < finish: + result = result + n + n = n + 1 + return result +``` + +En la función, las variables clave son `n` y `result`. Para encontrar el error hay que ver qué valores toman en cada iteración. + +Para eso existen los **depuradores visuales**. Se integran en los editores de código populares y permiten ejecutar el programa paso a paso, observando las variables en tiempo real. Se puede encontrar uno adecuado buscando «Python debuggers» en Google. + +En Hexlet, en lugar de un depurador se usa la **impresión de depuración**. El principio es el mismo, solo que los valores de las variables se muestran con el `print` normal. Lo que se imprime se ve en la pestaña `OUTPUT`. + +```python +def sum_of_series(start: int, finish: int) -> int: + result = 0 + n = start + while n < finish: + print("new iteration !!!!") + print(n) + result = result + n + n = n + 1 + print(result) + return result + + +sum_of_series(3, 5) + +# new iteration !!!! +# 3 +# 3 +# new iteration !!!! +# 4 +# 7 +``` + +La salida muestra que hay una iteración menos de las necesarias. El cinco (`finish`) no entró en la suma. En la condición está `n < finish` en lugar de `n <= finish`. Hay que cambiar el signo `<` por `<=`. + +Los desarrolladores que empiezan a menudo se disgustan por los errores y se consideran poco atentos. Errores tienen todos, tanto los juniors como los seniors. La diferencia está en con cuánta seguridad los encuentras. + +Los principiantes piensan que un buen desarrollador mira el código y entiende de inmediato qué va mal. Eso rara vez funciona en la práctica. Un fragmento de código sin contexto dice poca cosa. **Si quieres pedir consejo a un desarrollador experimentado, lo primero es mostrarle el mensaje de error.**