├── .gitignore ├── README.md ├── aproximacion.py ├── busqueda_binaria.py ├── caja_negra.py ├── enumeracion.py ├── factoriales.py ├── iteraciones.py ├── programas_ramificados.py └── readme_img ├── Grace-Murray-Hopper.jpg ├── Telar-de-Jacquard.jpg ├── alan-turing.jpg ├── arquitectura-neumann.png ├── busqueda-binaria.png ├── church_alonzo.jpg ├── computador-cuantico.png ├── computadora-griega.jpg ├── dennis-ritchie.jpg ├── eniac.jpg ├── feynman.jpeg ├── lovelace.jpg ├── maquina-tabuladora.jpg ├── microchips.jpg ├── motor-babbage.jpg ├── neumann-edvac.jpeg ├── nube.jpg ├── oblea-silicio.jpg ├── operador-logico.png ├── python.png ├── reserved-words-python.png └── van-rossum.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Introducción al Pensamiento Computacional con Python

3 |
4 | 5 |
6 | 7 |
8 | 9 | # Introducción al documento 10 | 11 | El contenido de este documento esta basado en el curso del mismo nombre dictado por [David Aroesti](https://github.com/jdaroesti) en [Platzi](https://platzi.com/r/karlbehrens/). 12 | 13 | # Tabla de contenido 14 | - [Introducción al pensamiento computacional](#Introducción-al-pensamiento-computacional) 15 | - [Introducción al cómputo](#Introducción-al-cómputo) 16 | - [Lenguajes de programación](#Lenguajes-de-programación) 17 | - [Introducción a Python](#Introducción-a-Python) 18 | - [Preparación de tu computadora](#Preparación-de-tu-computadora) 19 | - [Elementos básicos de Python](#Elementos-básicos-de-Python) 20 | - [Objetos, expresiones y tipos numéricos](#Objetos,-expresiones-y-tipos-numéricos) 21 | - [Asignación de variables](#Asignación-de-variables) 22 | - [Cadenas](#Cadenas) 23 | - [Entradas](#Entradas) 24 | - [Programas ramificados](#Programas-ramificados) 25 | - [Iteraciones](#Iteraciones) 26 | - [Bucles for](#Bucles-for) 27 | - [Programas numéricos](#Programas-numéricos) 28 | - [Representación de flotantes](#Representación-de-flotantes) 29 | - [Enumeración exhaustiva](#Enumeración-exhaustiva) 30 | - [Aproximación de soluciones](#Aproximación-de-soluciones) 31 | - [Búsqueda Binaria](#Búsqueda-Binaria) 32 | - [Funciones, alcance y abstracción](#Funciones,-alcance-y-abstracción) 33 | - [Funciones y abstracción](#Funciones-y-abstracción) 34 | - [Scope o Alcance](#Scope-o-Alcance) 35 | - [Especificaciones del código](#Especificaciones-del-código) 36 | - [Recursividad](#Recursividad) 37 | - [Funciones como objetos](#Funciones-como-objetos) 38 | - [Tipos estructurados, mutabilidad y funciones de alto nivel](#Tipos-estructurados,-mutabilidad-y-funciones-de-alto-nivel) 39 | - [Tuplas](#Tuplas) 40 | - [Rangos](#Rangos) 41 | - [Listas y mutabilidad](#Listas-y-mutabilidad) 42 | - [Diccionarios](#Diccionarios) 43 | - [Pruebas y debbugging](#Pruebas-y-debbugging) 44 | - [Pruebas de caja negra](#Pruebas-de-caja-negra) 45 | - [Pruebas de caja de cristal](#Pruebas-de-caja-de-cristal) 46 | - [Debugging](#Debugging) 47 | - [Excepciones y afirmaciones](#Excepciones-y-afirmaciones) 48 | - [Manejo de excepciones](#Manejo-de-excepciones) 49 | - [Excepciones como control de flujo](#Excepciones-como-control-de-flujo) 50 | - [Afirmaciones](#Afirmaciones) 51 | 52 | # Introducción al pensamiento computacional 53 | ## Introducción al cómputo 54 | 55 | Posiblemente la primera computadora fue creada por los **antiguos griegos** el cual tenia el propósito de calcular las posiciones del Sol, Luna y algunos otros cuerpos celestes. 56 | 57 |
58 |
59 | 60 |
Mecanismo Anticitera
61 |
62 |
63 | 64 | Miles de años después se creo el **telar de Jacquard**, en donde las se creaban tarjetas con agujeros que representaban la información que tiene que hacer un pedazo de tela. 65 | 66 |
67 |
68 | 69 |
Telar de Jacquard
70 |
71 |
72 | 73 | Después llego el **motor analítico de Charles Babbage**, el cual ocupo la tecnología de punta en su época para poder realizar cálculos. 74 | 75 |
76 |
77 | 78 |
Motor analítico de Charles Babbage
79 |
80 |
81 | 82 | A finales del siglo XIX el gobierno de EE.UU. tenia serios problemas para realizar los censos como mandaba la constitución. En este momento fue cuando llego la **máquina tabuladora**, la cual se utilizo para realizar los censos con tarjetas, obteniendo resultados mas rápidos y certeros. 83 | 84 |
85 |
86 | 87 |
Máquina Tabuladora
88 |
89 |
90 | 91 | Antiguamente existía la **profesión de computadora**, la cual eran personas que se dedicaban a seguir ciertas instrucciones para obtener los resultados. Sin embargo estos resultados estaban plagados de errores. Al inicio del siglo XX ya existían compañías que tenían la necesidad de realizar cálculos exactos y a gran escala. Es aquí donde llegan **Alan Turing** y **Alonzo Church** con la idea de que todos los algoritmos desarrollados por la humanidad podían ser reducidas a una maquina imaginaria, que tuviera una cinta infinita donde apuntarían símbolos y estos símbolos se pudieran manipular. Es aquí donde se comenzó la carrera para crear la primera computadora electrónica, el cual fue el **ENIAC**. 92 | 93 |
94 |
95 | 96 | 97 | 98 |
Alonzo Chruch, Alan Turing y la maquina ENIAC respectivamente.
99 |
100 |
101 | 102 | **John von Neumann** se dio cuenta de que en el hardware no solo se podía almacenar el poder de computo, también los programas para ejecutar. A esta arquitectura se le llama la **arquitectura de von Neumman.** De esta arquitectura nace la máquina **EDVAC** (Electronic Discrete Automatic Computer). 103 | 104 |
105 |
106 | 107 |
John von Neumann junto a la maquina EDVAC.
108 |
109 |
110 | 111 |
112 |
113 | 114 |
La arquitectura von Neumann.
115 |
116 |
117 | 118 | Con la llegada de los **microchips** llego la pauta para la computación de hoy en día. Estos microchips se hicieron tan pequeños con el tiempo usando la tecnología de la **fotónica.** 119 | 120 |
121 |
122 | 123 | 124 |
Microchip y Oblea de Silicio respectivamente.
125 |
126 |
127 | 128 | Ya en nuestros tiempos llego la **nube**, el cual son data centers que no son mas que miles o millones de computadoras. 129 | 130 |
131 |
132 | 133 |
Sala de servidores de una nube
134 |
135 |
136 | 137 | **Richar Feyman** nos dio las bases del computo cuántico, el pensaba que no podíamos simular los sistemas cuánticos sino teníamos una computadora cuántica, por lo cual hoy en día estamos en la carrera de la **computación cuántica.** 138 | 139 |
140 |
141 | 142 | 143 |
Richard Feyman y una computadora cuántica respectivamente.
144 |
145 |
146 | 147 | ## Lenguajes de programación 148 | 149 | ¿Cómo le damos instrucciones a las computadoras? Primero debemos saber que existen **conocimiento declarativo e imperativo.** El conocimiento **declarativo** define las relaciones que existen entre diversas variables, por ejemplo una fórmula matemática. En el caso del **imperativo** nos dice como llegar a un resultado, y dentro de este existen los **algoritmos.** 150 | 151 | Un **algoritmo** es una _lista finita de instrucciones_ que describen un cómputo, que cuando se ejecuta con ciertas entradas _(inputs)_ ejecuta pasos intermedios para llegar a un resultado _(output)_. Los algoritmos se conocen desde los antiguos griegos, y fue la evolución de estos que nos dieron los primeros **lenguajes de programación.** 152 | 153 | **Ada Lovelace** se dio cuenta de que con las bases teóricas del _motor analítico_ podía calcular una serie de los _números de Bernoulli_, y así creo el primer programa de computación. 154 | 155 |
156 |
157 | 158 |
Ada Lovelace
159 |
160 |
161 | 162 | **Grace Murray Hopper** fue pionera en el mundo de las ciencias de la computación y la primera programadora que utilizó el _Mark I_. Entre las décadas de los 50 y 60 desarrolló el primer compilador para un lenguaje de programación así como también propició métodos de validación. Grace se le ocurrió la idea de tomar unas instrucciones de 1 y 0 para simplificarlos en una instrucción mas entendible para las personas, idea que fue el punta pie inicial para los **lenguajes de programación modernos.** 163 | 164 |
165 |
166 | 167 |
Grace Murray Hopper
168 |
169 |
170 | 171 | En el sentido de la idea de los lenguajes de programación llega **Dennis Ritchie**, el cual fue el inventor del lenguaje _C_, posiblemente uno de los lenguajes mas importantes de la historia. 172 | 173 |
174 |
175 | 176 |
Dennis Ritchie
177 |
178 |
179 | 180 | 181 | **Guido van Rossum**, tenia en mente crear un lenguaje de programación que fuera lo mas comprensible posible, eliminando símbolos y sintaxis extrañas, cercano al lenguaje natural. Fue por esta idea en donde nació _Python_. 182 | 183 |
184 |
185 | 186 |
Guido van Rossum
187 |
188 |
189 | 190 | Los **lenguajes de programación** modernos se les conoce como **Turing completeness**, ya que implementan todos los principios para implementar cualquier tipo de _algoritmo._ 191 | 192 | Todos los **lenguajes** tienen: 193 | - **Sintaxis:** Define la secuencia de símbolos que está bien formada. 194 | - **Semántica estática:** Define que enunciados con sintaxis correcta tienen significado. 195 | - **Semántica:** Define el significado. En los lenguajes de programación solo hay un significado. 196 | 197 | # Introducción a Python 198 | 199 | ## Preparación de tu computadora 200 | 201 | Antes de comenzar este curso asegúrate de preparar tu entorno de trabajo para poder hacer todos los ejercicios. A continuación te compartiré los pasos que debes seguir para configurar tu computadora. 202 | 203 | Si estás usando Windows asegúrate de instalar lo siguiente en tu computadora: 204 | 205 | ### Python 3.7 (o superior) 206 | 1. Para obtener el instalador dirígete a [https://www.python.org/downloads/](https://www.python.org/downloads/) 207 | 2. Descarga el instalador y ejecútalo en tu computadora. 208 | 3. Habilita la casilla de verificación en Install launcher for all users y Add Python 3.8 to PATH. A continuación presiona en Install Now. Windows te solicitará permisos para instalar Python en tu computadora. 209 | 4. Al finalizar la instalación se abrirá una ventana, en ella deberás presionar en la opción Disable path length limit. Windows te solicitará permisos para realizar este cambio. 210 | 211 | ### Visual Studio Code 212 | 213 | Visual Studio Code es un editor de textos que tiene integradas varias herramientas que te ayudarán a desarrollar tus ejercicios con facilidad. Para obtenerlo en tu computadora, dirígete a: [https://code.visualstudio.com/](https://code.visualstudio.com/) 214 | 215 | 1. Realiza una instalación normal de Visual Studio code. 216 | 2. En Visual Studio Code dirígete al panel de Extensiones, se encuentra en el panel lateral izquierdo. Ahí deberás buscar la extensión llamada Python. 217 | 3. Selecciona la extensión creada por Microsoft. Una vez seleccionada, instálala. 218 | 4. Una vez instalada, reinicia Visual Studio Code. 219 | 220 | Listo con esto podrás correr los programas que escribas en python en la terminal de Visual Studio Code. 221 | 222 | ## Elementos básicos de Python 223 | 224 | En esta sección veremos los elementos básicos de Python, sin embargo estaremos _aprendiendo_ sobre los **elementos básicos** de cualquier lenguaje de programación. 225 | 226 | En los lenguajes tenemos definiciones como: 227 | 228 | - Bajo nivel vs alto nivel: 229 | **Bajo nivel** significa que esta diseñado para las **máquinas**. **Alto nivel** por su parte es orientado a los **humanos.** 230 | 231 | - General vs dominio específico: Los lenguajes **generales** tienen todos los primitivos de Turing para poder implementar y computar cualquier tipo de algoritmo. Por otro lado los de **dominio específico** son lenguajes especializados a tareas muy específicas. 232 | 233 | - Interpretado vs compilado: En los lenguajes **interpretados** mientras corre el programa se traduce la instrucción a lenguaje máquina que para ejecutar, en cambio para los lenguajes **compilados** estos toman todas las instrucciones y las traduce _antes_ a lenguaje máquina. 234 | 235 | Python es un lenguaje de _alto nivel, general e interpretado_. 236 | 237 | Los elementos básicos son: 238 | 239 | - **Literales:** son formas simples de inicializar objetos directamente en memoria. 240 | ``` 241 | literales = 1, 'abc', 2.0, True 242 | ``` 243 | 244 | - **Operadores:** son los operadores algebraicos. 245 | ``` 246 | operadores = + / % ** = == 247 | ``` 248 | 249 | Podemos interpolar _literales_ y _operadores_ en nuestros algoritmos. Si el significado dentro de nuestra instrucción no tiene sentido para el lenguaje nos devolverá el tipo de error que tengamos. Para iniciar en consola él interprete lo haremos escribiendo. 250 | 251 | ```bash 252 | python3 253 | ``` 254 | 255 | Con esto dentro de la consola podremos escribir código de Python. 256 | 257 | ```py 258 | >>> 1 + 2 259 | 3 260 | 261 | >>> 1 3.0 # error sintáctico 262 | File "", line 1 263 | 1 3.0 264 | ^ 265 | SyntaxError: invalid syntax 266 | 267 | >>> 5 / 'Texto' # error semántico estático 268 | Traceback (most recent call last): 269 | File "", line 1, in 270 | TypeError: unsupported operand type(s) for /: 'int' and 'str' 271 | 272 | >>> 5 * 'Texto' 273 | 'TextoTextoTextoTextoTexto' 274 | 275 | >>> print('Hello, world!') # statement o enunciado 276 | Hello, world! 277 | ``` 278 | 279 | ## Objetos, expresiones y tipos numéricos 280 | 281 | Vamos hablar de **objetos, expresiones y tipos numéricos.** 282 | 283 | - **Objetos**: son la abstracción más alta de cualquier lenguaje de programación, son la forma en que modelamos el mundo dentro de nuestros programas. Los objetos se encuentran en memoria y podemos referenciarlas con algún tipo de variable. 284 | 285 | - **Tipos**: los _objetos_ pueden ser enteros, flotantes, booleanos e incluso pueden ser objetos más complejos como el modelo de un humano con todos sus características y atributos. 286 | 287 | - **Escalares vs no escalares**: los **escalares** son datos que podemos subdividir en piezas fundamentales, estos pueden ser enteros, flotantes, etc. Los **no escalares** son los datos que no podemos _subdividir_. 288 | 289 | ```py 290 | >>> # Esta es una expresión 291 | 292 | 293 | >>> 'Estoy' + 'Programando!' # Con esta expresión 294 | 'EstoyProgramando!' # Obtenemos este valor 295 | 296 | >>> 2 + 2 # Con esta expresión 297 | 4 # Obtenemos este valor 298 | ``` 299 | 300 | En Python podemos ver los tipos de datos que contienen nuestros objetos _(variables)_, para esto usamos la función **Type.** 301 | 302 | ```bash 303 | >>> my_int = 1 304 | >>> my_float = 1.0 305 | >>> my_bool = False 306 | >>> my_none = None 307 | 308 | # Imprimiremos los tipos de nuestras variables. 309 | 310 | >>> type(my_int) 311 | 312 | 313 | >>> type(my_float) 314 | 315 | 316 | >>> type(my_bool) 317 | 318 | 319 | >>> type(my_none) 320 | 321 | ``` 322 | 323 | ## Asignación de variables 324 | 325 | Las **variables** son simplemente _nombres_ que se vinculan con un _valor en memoria_, y la forma en la que los _vinculamos_ es a través del **operador de asignación (=)**, y para _comparar_ su valor utilizamos **2 veces el operador de asignación (==).** La forma correcta de nombrar nuestras variables es darles un nombre _descriptivo_. 326 | 327 | ```py 328 | # Tenemos unas variables que no entendemos que representan 329 | a = 2 330 | x = 4 331 | z = (a * x) / 2 332 | 333 | # Y las cambiamos por unas mas descriptivas 334 | base = 2 335 | altura = 4 336 | area = (base * altura) / 2 337 | ``` 338 | 339 | También podemos **reasignar** valores a nuestras _variables_. 340 | 341 | ```bash 342 | # A my_var le asignamos un valor 343 | >>> my_var = 'Hello, world' 344 | >>> print(my_var) 345 | Hello, world 346 | 347 | # Luego reasignamos otro valor 348 | >>> my_var = 3 349 | >>> print(my_var) 350 | 3 351 | ``` 352 | 353 | Cuando el espacio en memoria ya no tiene ninguna variable que la referencie, el **garabage collector** libera este espacio. 354 | 355 | Cada uno de los lenguajes de programación tiene sus reglas. Algunas reglas para las variables en Python son: 356 | 357 | - Pueden contener mayúsculas, minúsculas, números(sin comenzar con uno) y el símbolo _ 358 | - No pueden llamarse como las **palabras reservadas**. 359 | 360 | Los lenguajes tienen algo llamado **palabras reservadas**, estas son objetos dentro del lenguaje que ya tienen alguna función o valor asignado. 361 | 362 |
363 |
364 | 365 |
366 |
367 | 368 | ## Cadenas 369 | 370 | Las **cadenas** son secuencias de caracteres. 371 | 372 | ```py 373 | '123' #Esta es una cadena 374 | ``` 375 | 376 | Los _operadores_ que utilizamos tienen otros significados. Cuando utilizamos el operador **multiplicar (*)** lo que haremos es multiplicar la cadena por el numero de veces que deseamos, y con el operador **suma (+)** concatenaremos varias cadenas, sin embargo _Python_ nos permite concatenar de una forma mas legible. 377 | 378 | ```py 379 | '123' * 3 # Con el operador * 380 | '123123123' # Obtenemos este resultado. 381 | 382 | '123' + '456' # Y el operador + 383 | '123456' # Concatenara las cadenas. 384 | 385 | ('Hip ' * 3) + 'hurra' # También podemos combinar operadores 386 | 'Hip Hip Hip hurra' 387 | 388 | f'{"Hip " * 3}hurra' # En Python podemos usar la expresión f para concatenar 389 | 'Hip Hip Hip hurra' 390 | ``` 391 | 392 | A las cadenas les podemos asignar diversas funciones: 393 | 394 | - len: nos indica la longitud de la cadena. 395 | - indexing: con esto podemos acceder a cada uno de los elementos de esta cadena a través de indices. 396 | - slicing: podemos dividir las cadenas subcadenas. 397 | 398 | ```py 399 | my_str = 'Hello, world!' # Creamos una cadena 400 | 401 | len(my_str) # Consultamos por su longitud 402 | 13 403 | 404 | my_str[0] # Con slicing consultamos por el 1er caracter. 405 | 'H' 406 | 407 | my_str[1] # Consultamos por el 2do caracter. 408 | 'e' 409 | 410 | my_str[2] # Consultamos por el 3er caracter. 411 | 'l' 412 | 413 | my_str[2:] # Traemos desde el 3er caracter hasta el final. 414 | 'llo, world!' 415 | 416 | # Es importante indicar que los finales no son inclusivos. 417 | 418 | my_str[:3] # Tremos desde el principio hasta el 3ro. 419 | 'Hel' 420 | 421 | my_str[2:5] # Traemos desde el 3er caracter hasta el 5to. 422 | 'llo' 423 | 424 | my_str[::2] # Traemos desde el principio hasta el final saltando de 2 en 2. 425 | 'Hlo ol!' 426 | ``` 427 | 428 | - Los objetos de tipo str pueden representarse con _comillas dobles (")_ o _comillas simples (')_ 429 | - El operador _suma (+)_ tiene diferente significado según el tipo de dato. Con **cadenas** significa _concatenación_. 430 | - El operador _multiplicación (*)_ es el operador de _repetición_ con **cadenas**. 431 | - Las cadenas son **inmutables**. Esto significa que una vez que creamos una cadena en memoria esta ya no puede cambiar, podemos reasignar la variable que la referencia a otro valor, pero la cadena en memoria no cambiara. 432 | 433 | ## Entradas 434 | 435 | Las **entradas** son una forma recibir información para que las computadoras logren realizas cómputos. 436 | 437 | - Python tiene la función input para recibir datos del usuario del programa 438 | - Input siempre regresa cadenas, por lo que si queremos utilizar otro tipo, tenemos que hacer _type casting_. El _type casting_ es **transformar** el tipo de dato en otro, con esto podemos transformar el tipo y guardarlo en memoria asignándolo a una variable. 439 | 440 | ```bash 441 | nombre = input('Cual es tu nombre: ') # Utilizamos input para ingresar un nombre 442 | Cual es tu nombre: Karl 443 | 444 | print(nombre) # Vemos que contiene nuestra variable nombre 445 | Karl 446 | 447 | print(f'Tu nombre es {nombre}') # Imprimimos una cadena concatenando una oración con nuestra variable. 448 | Tu nombre es Karl 449 | 450 | numero = input('Escribe un numero: ') # Utilizamos input para ingresar un numero 451 | Escribe un numero: 45 452 | 453 | numero # Vemos que contiene nuestra variable numero 454 | '45' 455 | 456 | type(numero) # Vemos el tipo de dato que es numero 457 | # Y vemos que es un str 458 | 459 | numero = int(input('Escribe un numero: ')) # Pero si definimos previamente el input como int 460 | Escribe un numero: 45 461 | 462 | type(numero) # Nuestra variable numero sera de tipo int 463 | 464 | ``` 465 | 466 | ## Programas ramificados 467 | 468 | Para que nuestros programas realicen trabajos interesantes estos deben ser capaces de tomar decisiones, test o pruebas, es desde este concepto donde salen las **ramificaciones.** Dentro de los test que podemos realizar son los operadores de **comparación** y estos nos devolverás si la comparación es **verdadera (True)** o **falsa (False).** 469 | 470 | - **Igual (==)**: Lo utilizaremos para comparar 2 objetos. 471 | - **Distinto (!=)**: Verificamos que los objetos sean distintos. 472 | - **Mayor que (>)**: Igual que en álgebra, comparamos si el primer termino es mayor que el segundo. 473 | - **Menor que (<)**: Verificamos que el primer termino sea menor que el segundo. 474 | - **Mayor igual que (>=)**: Verificamos que el primer termino sea mayor igual al segundo. 475 | - **Menor igual que (<=)**: Verificamos que el primer termino sea menor igual al segundo. 476 | 477 | Además de los operadores de comparación también tenemos los operadores lógicos, estos son 3 **(and, or, not).** 478 | 479 |
480 |
481 | 482 |
483 |
484 | 485 | Una vez que podemos entender bien los operadores de comparación y lógicos podemos generar nuestros **programas ramificados.** Una forma típica de ocupar los operadores es con el método **if.** 486 | 487 | ```py 488 | if condition: # Evaluamos en primera instancia una condición. 489 | expresion 490 | elif: # En caso de que no se cumpla la condición anterior evaluamos nuevamente con otra. 491 | expresion 492 | else: # En caso de que no se cumpla ninguna condición. 493 | expresion 494 | 495 | # En el ejemplo anterior pueden es obligatorio el 'if', sin embargo 'elif' 496 | # y 'else' son opcionales. Pueden existir cuantos 'elif' queramos, pero solo 497 | # puede haber 1 'if' y 1 'else'. 498 | 499 | if 4 > 5: 500 | ... 501 | elif 4 < 5: 502 | print('4 es menor que 5') 503 | else: 504 | ... 505 | ``` 506 | 507 | Para poner en práctica esto crearemos un archivo _programas_ramificados.py_ y dentro de el escribiremos: 508 | 509 | ```py 510 | num_1 = int(input('Escoge un entero: ')) # Preguntamos por un primer número. 511 | num_2 = int(input('Escoge otro entero: ')) # Luego preguntamos por un segundo número. 512 | 513 | if num_1 > num_2: # Si el primer número es mayor que el segundo. 514 | print('El primer número es mayor que el segundo.') # Imprimimos esta expresión. 515 | elif num_1 < num_2: # En caso de que el segundo sea mayor. 516 | print('El segundo número es mayor que el primero.') # Imprimiremos esta expresión. 517 | else: # En caso de que no cumpla ninguna condición. 518 | print('Los 2 números son iguales.') 519 | ``` 520 | 521 | Para ejecutar nuestro programa iremos a la terminal y escribiremos 522 | 523 | ``` 524 | python3 la/dirección/relativa/de/tu/archivo/programas_ramificados.py 525 | ``` 526 | 527 | y en consola nos preguntara nuestros números y nos dará un resultado 528 | 529 | ``` 530 | Escoge un entero: 8 531 | Escoge otro entero: 4 532 | El primer número es mayor que el segundo. 533 | ``` 534 | ``` 535 | Escoge un entero: 7 536 | Escoge otro entero: 10 537 | El segundo número es mayor que el primero. 538 | ``` 539 | ``` 540 | Escoge un entero: 4 541 | Escoge otro entero: 4 542 | Los 2 números son iguales. 543 | ``` 544 | 545 | ## Iteraciones 546 | 547 | Las **iteraciones** nos permiten repetir las operaciones de una manera simple. 548 | 549 | - La mayoría de las tareas computacionales no se pueden lograr con ramificaciones. 550 | - Cuando queremos que un programa haga lo mismo varias veces, utilizaremos iteraciones. 551 | - Se pueden escribir iteraciones dentro de iteraciones. 552 | - Podemos utilizar _break_ para salir de una iteración. 553 | - Tener cuidado de iteraciones infinitas. 554 | 555 | Para poner en práctica las iteración crearemos el archivo _iteraciones.py_ 556 | 557 | ```py 558 | contador = 0 559 | 560 | while contador < 10: 561 | print(contador) 562 | contador += 1 # contador = contador + 1 563 | ``` 564 | 565 | Luego iremos a la consola para ejecutar nuestro archivo. 566 | 567 | ``` 568 | python3 la/dirección/relativa/de/tu/archivo/iteraciones.py 569 | ``` 570 | 571 | Y veremos que en nuestra consola se imprimirán los números del 0 al 9. 572 | 573 | Si queremos que nuestro programa salga de la iteración cuando se cumpla cierta condición usaremos **break.** 574 | 575 | ```py 576 | contador = 0 577 | 578 | while contador < 10: 579 | print(contador) 580 | contador += 1 # contador = contador + 1 581 | 582 | if contador > 6: # Cuando contador sea mayor que 6 terminara la iteración. 583 | break 584 | ``` 585 | 586 | ## Bucles for 587 | 588 | Los bucles, en diversos lenguajes de programación pueden ser definidos o indefinidos. Los bucles definidos preestablecen las condiciones de la iteración por adelantado. Por su parte, los bucles indefinidos establecen la condición en la que una iteración terminará. En este último tipo de bucles existe el riesgo de que el bucle se vuelva infinito (cuando la condición de suspensión nunca se cumple). 589 | 590 | Los bucles definidos se implementan en Python a través del keyword **for**. Por su parte, los bucles indefinidos se implementan con el keyword **while**. 591 | 592 | Sin embargo, esta no es la única forma de implementar bucles definidos. Por ejemplo, Javascript puede implementar un bucle definido mediante el siguiente constructo: 593 | 594 | ```py 595 | for (i = 0; i <= 10; i++) { 596 | 597 | } 598 | ``` 599 | 600 | El bucle se puede leer de la siguiente manera: 601 | 602 | - Inicializa el bucle en 0 603 | - Continua el bucle mientras **i** sea menor o igual que 10 604 | - Incrementa i en uno al final de cada iteración 605 | 606 | Es importante señalar que la expresión **i++** es equivalente a lo que en Python escribiríamos como **i += 1.** 607 | 608 | Una segunda forma de crear un bucle definido es iterando en una colección de objetos. Esta es la forma que Python utiliza: 609 | 610 | ```py 611 | for in : 612 | 613 | ``` 614 | 615 | ### El bucle for en Python 616 | 617 | En la definición anterior debemos entender <_iterable_> como una colección de objetos; y la <_variable_> como el elemento específico que se está exponiendo mediante el bucle en cada iteración. 618 | 619 | ```py 620 | >>> frutas = ['manzana', 'pera', 'mango'] 621 | >>> for fruta in frutas: 622 | print(fruta) 623 | 624 | 625 | manzana 626 | pera 627 | mango 628 | ``` 629 | 630 | ### Iterables 631 | 632 | En Python, un iterable es un objeto que se puede utilizar en un bucle definido. Si un objeto es iterable significa que se puede pasar como argumento a la función **iter**. El **iterable** que se pasa como parámetro a la función **iter** regresa un **iterator**. 633 | 634 | ```py 635 | >>> iter('cadena') # cadena 636 | >>> iter(['a', 'b', 'c']) # lista 637 | >>> iter(('a', 'b', 'c')) # tupla 638 | >>> iter({'a', 'b', 'c'}) # conjunto 639 | >>> iter({'a': 1, 'b': 2, 'c': 3}) # diccionario 640 | ``` 641 | 642 | Todas las llamadas anteriores regresan un objeto de tipo **iterator**. 643 | 644 | ¿Qué pasa si le pasamos a la función **iter** un objeto que no en **iterable**? Obtendremos un **TypeError** que señala que el objeto no es un **iterable**. Esto es un ejemplo de programación defensiva en el que Python verifica el tipo del objeto antes de proceder al cómputo. ¡Intentalo en tu consola! 645 | 646 | Es importante señalar que estos no son los únicos tipos de objetos que pueden ser **iterable**. Existen gran cantidad de ejemplos en la librería estándar y, de hecho, casi cualquier objeto se puede convertir en un **iterable** (pero eso ya lo veremos cuando hablemos de Python avanzado). 647 | 648 | ### Iterators 649 | 650 | Ahora que ya sabemos cómo obtener un **iterator**, ¿Qué podemos hacer con él? Un **iterator** es un objeto que regresa sucesivamente los valores asociados con el iterable. 651 | 652 | ```py 653 | >>> frutas = ['manzana', 'pera', 'mango'] 654 | >>> iterador = iter(frutas) 655 | >>> next(iterador) 656 | manzana 657 | >>> next(iterador) 658 | pera 659 | >>> next(iterador) 660 | mango 661 | ``` 662 | 663 | Como puedes ver, el **iterator** guarda el estado interno de la iteración, de tal manera que cada llamada sucesiva a **next** regresa el siguiente elemento. ¿Qué pasa una vez que ya no existan más elementos en el **iterable**? La llamada a **next** arrojará un error de tipo **StopIteration**. 664 | 665 | ### ¿Cómo implementa Python los bucles definidos? 666 | 667 | Ahora ya conocemos todos los elementos necesarios para entender que es lo que sucede en Python cuando ejecutamos un bucle **for**. Considera nuevamente el siguiente código: 668 | 669 | ```py 670 | >>> frutas = ['manzana', 'pera', 'mango'] 671 | >>> for fruta in frutas: 672 | print(fruta) 673 | ``` 674 | 675 | Este bucle se puede describir con los conceptos que explicamos previamente: 676 | 677 | 1. Python llama internamente la función **iter** para obtener un **iterator** 678 | 2. Una vez que tiene un **iterator** llama repetidamente la función next para tener acceso al siguiente elemento en el bucle. 679 | 3. Detiene el bucle una vez que se arroja el error **StopIteration**. 680 | 681 | ### Bucles for con diccionarios 682 | 683 | Para iterar a lo largo de un diccionario tenemos varias opciones: 684 | 685 | - Ejecutar el bucle **for** directamente en el diccionario, lo cual nos permite iterar a lo largo de las llaves del diccionario. 686 | - Ejecutar el bucle **for** en la llamada **keys** del diccionario, lo cual nos permite iterar a lo largo de las llaves del diccionario. 687 | - Ejecutar el bucle **for** en la llamada **values** del diccionario, lo cual nos permite iterar a lo largo de los valores del diccionario. 688 | - Ejecutar el bucle **for** en la llamada **items** del diccionario, lo cual nos permite iterar en una tupla de las llaves y los valores del diccionario. 689 | 690 | ```py 691 | estudiantes = { 692 | 'mexico': 10, 693 | 'colombia': 15, 694 | 'puerto_rico': 4, 695 | } 696 | 697 | for pais in estudiantes: 698 | ... 699 | 700 | for pais in estudiantes.keys(): 701 | ... 702 | 703 | for numero_de_estudiantes in estudiantes.values(): 704 | ... 705 | 706 | for pais, numero_de_estudiantes in estudiantes.items(): 707 | ... 708 | ``` 709 | 710 | ### Modificación del comportamiento de un bucle for 711 | 712 | Podemos modificar el comportamiento de un bucle **for** mediante los _keywords_ **break** y **continue**. 713 | 714 | **break** termina el bucle y permite continuar con el resto del flujo de nuestro programa. 715 | 716 | **continue** termina la iteración en curso y continua con el siguiente ciclo de iteración. 717 | 718 | ### Conclusiones 719 | 720 | Como pudimos observar, Python implementa los bucles definidos mediante los bucles **for**. Esta implementación nos permite iterar a lo largo de cualquier objeto que sea iterable. Para iterar necesitamos un iterador que nos regresará el siguiente valor en cada iteración. Todo esto, Python lo puede hacer por nosotros con el constructo **for ... in ...**. 721 | 722 | # Programas numéricos 723 | 724 | ## Representación de flotantes 725 | 726 | La mayoría del tiempo los números flotantes (tipo **float**) son una muy buena aproximación de los números que queremos calcular con nuestras computadoras. Sin embargo, “la mayoría del tiempo” no significa todo el tiempo, y cuando no se comportan de esta manera puede tener consecuencias inesperadas. 727 | 728 | Por ejemplo, trata de correr el siguiente código: 729 | 730 | ```py 731 | x = 0.0 732 | for i in range(10): 733 | x += 0.1 734 | 735 | if x == 1.0: 736 | print(f'x = {x}') 737 | else: 738 | print(f'x != {x}') 739 | ``` 740 | 741 | Es probable que te hayas sorprendido con el resultado. La mayoría de nosotros esperaríamos que imprimiera **1.0** en vez de **0.999999999999**. ¿Qué es lo que pasó?. 742 | 743 | Para entender qué es lo que pasó tenemos que entender que es lo que pasa en la computadora cuando realizamos cómputos con números flotantes. Y para eso necesitamos entender números binarios. 744 | 745 | Cuando aprendiste a contar, lo que en realidad aprendiste es una técnica combinatoria para manipular los siguientes símbolos que le llamamos números: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. 746 | 747 | La forma en la que funciona esta técnica es asignando el número 10 a la 0 al número de la extrema derecha, 10 a la 1 al siguiente, 10 a la 2 al siguiente y así sucesivamente. De tal manera que el número 525 es simplemente la representación de **(5 * 100) + (2 * 10) + (5 * 1)**. 748 | 749 | Esto nos dice que el número de números que podemos representar depende de cuanto espacio tengamos. Si tenemos un espacio de 3, podemos representar 1,000 números (10 elevado a la 3) o la secuencia del 0 al 999. Si tenemos 4, podemos representar 10,000 (10 elevado a la 4) o la secuencia del 0 al 9,999. De manera general podemos decir que con una secuencia de tamaño n, podemos representar 10 elevado a la n números. 750 | 751 | Los números binarios funcionan de la misma manera (de hecho cualquier número en cualquier base, por ejemplo, octales o hexadecimales). La única diferencia es cuántos símbolos tenemos para representar. En binario nada más tenemos 0, 1; 752 | en hexadecimal tenemos 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. 753 | 754 | De esta manera podemos decir que el número de la extrema derecha es **cantidad_de_simbolos^0**, **cantidad_de_simbolos^1**, **cantidad_de_simbolos^2**, etc. Por lo que en binario, que nada más tenemos 2 símbolos, decimos **2^0**, **2^1**, **2^2**, etc. Por ejemplo el número binario 101 es la representación de **(1 * 4) + (0 * 2) + (1 * 1)**, es decir 5. 755 | 756 | Esta representación nos permite trabajar con todos los números positivos enteros dentro del computador, pero ¿Qué hacemos con los negativos y los racionales?. 757 | 758 | El caso de los números negativos es sencillo: simplemente agregamos un bit adicional que representa el signo y la añadimos en la extrema izquierda. Por lo que el número **0**101 sería +5 y el número **1**101 sería -5. 759 | 760 | El caso de los racionales es más complejo. En la mayoría de los lenguajes de programación modernos los racionales utilizan una implementación llamada punto flotante. ¿Cómo funciona esta representación?. 761 | 762 | Antes de pasar a binario, vamos a pretender que estamos trabajando con una computadora basada en decimales. Un número flotante lo representaríamos con un par de enteros: los dígitos significativos y el exponente. Por ejemplo, el número 2.345 se representaría como **(2345 * 10^-3)** o **(2345, -3)**. 763 | 764 | El número de dígitos significativos determinan la precisión con la que podemos representar número. Por ejemplo si nada más tuviéramos dos dígitos significativos el número 2.345 no se podría representar de manera exacta y tendríamos que convertirlo a una aproximación, en este caso 2.3. 765 | 766 | Ahora pasemos a la verdadera representación interna de la computadora, que es en binario. ¿Cómo representarías el número 5/8 o 0.625? Lo primero que tenemos que saber es que 5/8 es en realidad el número **5 * 2^-3.** Por lo que podríamos decir (101, -11) (recuerda que el número 5 es 101 en binario y el 3 es 11). 767 | 768 | Regresemos a nuestro problema inicial: ¿Cómo representaremos 1/10 (que escribimos en Python cómo 0.1)? Lo mejor que podemos hacer con cuatro dígitos significativos es (0011, -101) que es equivalente a 3/32 (0.09375). ¿Qué tal si tuviéramos cinco dígitos significativos? La mejor representación sería (11001, -1000) que es equivalente a 25/256 (0.09765625). ¿Cuántos dígitos significativos necesitamos entonces? Un número infinito. No existe ningún número que cumpla con la siguiente ecuación: **sim * 2^-exp**. 769 | 770 | En la mayoría de las implementaciones de Python tenemos 53 bits de precisión para números flotantes. Así que los dígitos significativos para representar el número 0.1 es igual a: 771 | 772 | 11001100110011001100110011001100110011001100110011001 que es equivalente al número decimal: 0.1000000000000000055511151231257827021181583404541015625 773 | 774 | Muy cercano a 1/10 pero no exactamente 1/10. Ahora ya sabemos la razón de esa respuesta tan extraña. Hay muy pocas situaciones en la que 1.0 es aceptable, pero 0.9999999999999999 no. Pero ¿Cuál es la moraleja de esta historia? 775 | 776 | Hasta ahora hemos verificado igualdad con el operador **==**. Sin embargo, cuando estamos trabajando con flotantes es mejor asegurarnos que los números sean aproximados en vez de idénticos. Por ejemplo **x < 1.0 and x > 0.99999**. 777 | 778 | ## Enumeración exhaustiva 779 | 780 | También llamado "adivina y verifica" donde simplemente generamos todas las posibilidades. Técnicamente este no es un algoritmo eficiente, sin embargo, dependiendo del universo de posibilidades puede ser que sea el mas adecuado, ya que _las computadoras actuales son muy rápidas_ y por lo tanto la eficiencia de nuestro programa no es relevante, por lo tanto siempre ten en mente este tipo de **algoritmo** como uno de los **primeros en implementar**. 781 | 782 | Vamos a crear un ejemplo de enumeración exhaustiva buscando la raíz cuadrada exacta de un numero. 783 | 784 | ```py 785 | objetivo = int(input('Escoge un entero: ')) 786 | 787 | """Inicializamos respuesta como 0""" 788 | respuesta = 0 789 | 790 | """Mientras respuesta^2 sea menor que nuestro numero objetivo.""" 791 | while respuesta**2 < objetivo: 792 | respuesta += 1 # Respuesta aumentara en 1. 793 | 794 | if respuesta**2 == objetivo: 795 | print(f'La raíz cuadrada de {objetivo} es {respuesta}') 796 | 797 | else: 798 | print(f'{objetivo} no tiene una raíz cuadrada exacta') 799 | ``` 800 | 801 | ## Aproximación de soluciones 802 | 803 | Es similar a la enumeración exhaustiva, pero no necesita una respuesta exacta, por lo tanto podemos aproximar soluciones con un margen de error que llamaremos **epsilon**. 804 | 805 | Como siempre en programación debemos hacer un _trade-off_, no podemos ser precisos y rápidos a la vez, por lo tanto cuando nuestro **epsilon** es muy pequeño esto significa que debemos realizar **mas iteraciones** para llegar a la aproximación, lo cual significa sacrificar tiempo. Y por otro lado si queremos que nuestro **tiempo de ejecución** sea lo **mas corto posible** debemos sacrificar la **precisión** aumentando el valor de **epsilon**. 806 | 807 | ```py 808 | objetivo = int(input('Escoge un numero: ')) 809 | 810 | epsilon = 0.01 # Definimos un margen de error. 811 | paso = epsilon**2 # Los pasos para buscar la raíz sera igual a epsilon^2 812 | respuesta = 0 # Inicializamos una respuesta 0 813 | 814 | 815 | while abs(respuesta**2 - objetivo) >= epsilon and respuesta <= objetivo: 816 | respuesta += paso 817 | 818 | if abs(respuesta**2 - objetivo) >= epsilon: 819 | print(f'No se encontró la raiz cuadrada de {objetivo}') 820 | else: 821 | print(f'La raiz cuadrada de {objetivo} es {respuesta}') 822 | ``` 823 | 824 | Puedes intentar ir moviendo la magnitud de epsilon para obtener una mejor precisión o mejorar el tiempo de ejecución. 825 | 826 | ## Búsqueda Binaria 827 | 828 | Cuando la respuesta se encuentra en un conjunto ordenado, podemos utilizar **búsqueda binaria**. Es altamente eficiente, pues corta el espacio de búsqueda en dos por cada iteración. Los pasos que sigue son: 829 | 830 | 1. Consideramos como segmento inicial de búsqueda a la lista completa. 831 | 2. Analizamos el punto medio del segmento (el valor central), si es el valor buscado, devolvemos el índice del punto medio. 832 | 3. Si el valor central es mayor al buscado, podemos descartar el segmento que está desde el punto medio hacia la a derecha. 833 | 4. Si el valor central es menor al buscado, podemos descartar el segmento que está desde el punto medio hacia la izquierda. 834 | 5. Una vez descartado el segmento que no nos interesa, volvemos a analizar el segmento restante, de la misma forma. 835 | 6. Si en algún momento el segmento a analizar tiene longitud 0 o negativa significa que el valor buscado no se encuentra en la lista. 836 | 837 | Para verlo de forma gráfica buscaremos el valor 18 en la lista [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]. 838 | 839 |
840 |
841 | 842 |
843 |
844 | 845 | Para realizar un ejemplo práctico crearemos un programa para buscar raíces cuadradas. 846 | 847 | ```py 848 | objetivo = int(input('Escoge un numero: ')) 849 | 850 | epsilon = 0.01 # Definimos nuestro margen de error. 851 | 852 | bajo = 0.0 # Inicializamos la parte baja de nuestra búsqueda como 0 853 | alto = max(1.0, objetivo) # Entre el numero que ingresamos y 1 vamos a tomar el mayor valor. 854 | respuesta = (alto + bajo) / 2 # Definimos la mitad entre bajo y alto. 855 | 856 | # Mientras el margen de error sea mayor a epsilon. 857 | while abs(respuesta**2 - objetivo) >= epsilon: 858 | 859 | # Si ((alto+bajo)/2)^2 es menor a nuestro numero objetivo 860 | if respuesta**2 < objetivo: 861 | 862 | # Definimos la parte baja de nuestra búsqueda como (alto + bajo)/2 863 | bajo = respuesta 864 | 865 | # En caso que (alto+bajo)/2 es mayor a nuestro numero objetivo 866 | else: 867 | # Definimos la parte baja de nuestra búsqueda como (alto + bajo)/2 868 | alto = respuesta 869 | 870 | # Luego definimos nuevamente la mitad entre alto y bajo. 871 | respuesta = (alto + bajo) / 2 872 | 873 | # Cuando el ciclo ya alcance un error menor a epsilon imprimiremos el resultado. 874 | print(f'La raíz cuadrada de {objetivo} es {respuesta}') 875 | ``` 876 | 877 | Este algoritmo es extremadamente rápido en comparación a los anteriores y esto es justamente lo que lo hace uno de los mas potentes. 878 | 879 | # Funciones, alcance y abstracción 880 | 881 | ## Funciones y abstracción 882 | 883 | La **abstracción** significa que no necesitas entender como funciona algo para utilizarlo. Un ejemplo de esto es una _calculadora_, en este caso no muchos saben como funciona el circuito interno de una calculadora, sin embargo esto no nos limita a utilizarlo, ya que solo necesitamos saber como operarlo. 884 | 885 | Una de las habilidades mas importantes en la programación es la **abstracción**, ya que utilizaremos la mayoría del tiempo códigos y librerías de otras personas, por lo que solamente debemos saber operarlos. 886 | 887 | La **decomposición** nos permite dividir el código en **componentes (funciones)** que colaboran con un fin en común. Esto se puede pensar como mini programas dentro de un programa mayor. 888 | 889 | Para poder escribir una **función** en _Python_ lo hacemos con **def** 890 | 891 | ```py 892 | """Las funciones se definen con 'def' luego del nombre y los parámetros que necesitara.""" 893 | def nombre(parámetros): 894 | 895 | """Ejecutamos las expresiones que necesitemos""" 896 | cuerpo 897 | 898 | """Y retornaremos el valor que queramos. El return no es obligatorio.""" 899 | return expresion 900 | 901 | """Aquí definimos una función suma""" 902 | def suma(a, b): 903 | total = a + b 904 | return total 905 | 906 | """Y para ejecutarlo simplemente llamamos a la 907 | función por su nombre e ingresamos los parámetros que necesita""" 908 | suma(2, 3) 909 | ``` 910 | En las funciones existen los **valores por defecto**, esto significa que en caso de que no se ingrese el argumento este ya tendrá un **valor por defecto.** También existen los keywords que significa que al llamar la función podemos llamar al nombre del argumento para asignarles un valor. 911 | 912 | ```py 913 | """Definimos una función con valor por defecto de "inverso = False""" 914 | def nombre_completo(nombre, apellido, inverso=False) 915 | if inverso: 916 | return f'{apellido} {nombre}' 917 | else: 918 | return f'{nombre} {apellido}' 919 | 920 | """De forma ordena ingreso los valores a los parámetros de la función. 921 | Sin embargo no es necesario ingresar un valor para "inverso", ya que 922 | tiene un valor por defecto ya asignado""" 923 | nombre_completo('Karl', 'Behrens') 924 | 925 | """En este caso ingresaremos el valor 'True' para 'inverso' """ 926 | nombre_completo('Karl', 'Behrens', inverso=True) 927 | 928 | """Con Keywords podemos ingresar las variables en el orden que 929 | deseamos mientras llamemos el valor del parámetro y 930 | le asignamos el valor.""" 931 | nombre_completo(apellido='Behrens', nombre='Karl') 932 | ``` 933 | 934 | ## Scope o Alcance 935 | 936 | Cuando ejecutamos una función esto significa que el lenguaje de programación entrara en un nuevo contexto de ejecución, y en este contexto se le asigna ciertas variables de los cuales tenemos acceso a través del código. Sin importar que declaremos una variable, si no hacemos uso de este no tendremos acceso a él. 937 | 938 | ```py 939 | def func1(un_arg, una_func): 940 | def func2(otro_arg): 941 | return otro_arg * 2 942 | 943 | valor = func2(un_arg) 944 | return una_func(valor) 945 | 946 | un_arg = 1 947 | 948 | def cualquier_func(cualquier_arg): 949 | return cualquier_arg + 5 950 | 951 | func1(un_arg, cualquier_func) 952 | ``` 953 | 954 | El uso de las variables y acceso a él a lo largo de nuestro código se llama **scope.** 955 | 956 | ## Especificaciones del código 957 | 958 | La especificación del código es un **comentario** en el que informamos de forma **explicita** y **concisa** lo que realizan nuestras instrucciones. Tenemos que tener 3 items importantes en la documentación: 959 | 960 | - Que hace la instrucción. 961 | - Que significan los parámetros. 962 | - Que es lo que devuelve nuestra instrucción. 963 | 964 | ```py 965 | def suma(a, b): 966 | """Suma dos valores a y b 967 | 968 | param int a cualquier entero 969 | param int b cualquier entero 970 | returns la sumatoria de a y b 971 | """ 972 | 973 | total = a + b 974 | return total 975 | ``` 976 | 977 | ## Recursividad 978 | 979 | La recursividad se puede definir de 2 formas: 980 | 981 | - **Algorítmica:** Una forma de crear soluciones utilizando el principio de "divide y vencerás". Esto significa que podemos resolver un problema dividiéndolo en pequeñas versiones mas simple e ir resolviéndolos de a poco. 982 | 983 | - **Programática:** Una técnica programática mediante la cual una función se llama a sí misma. 984 | 985 | Para ver la recursividad **programática** lo haremos dentro de un ejemplo práctico. En este ejemplo programaremos una función para devolver el valor de un numero _factorial._ 986 | 987 | ```py 988 | def factorial(n): 989 | """Calcula el factorial de n 990 | 991 | n int > 0 992 | return n! 993 | """ 994 | 995 | # Para que la recursividad no sea infinita 996 | # definimos en que momento terminara. 997 | if n == 1: 998 | return 1 999 | 1000 | # Llamamos a la función "factorial" a si misma 1001 | # pero el valor de n va disminuyendo en 1 1002 | # a medida que se repite su llamado. 1003 | return n * factorial(n - 1) 1004 | 1005 | n = int(input('Escribe un entero: ')) 1006 | 1007 | print(factorial(n)) 1008 | ``` 1009 | 1010 | ## Funciones como objetos 1011 | 1012 | Una de las características más poderosas de Python es que todo es un objeto, incluyendo las funciones. Las funciones en Python son “ciudadanos de primera clase”. 1013 | 1014 | Esto, en sentido amplio, significa que en Python las funciones: 1015 | 1016 | - Tienen un tipo 1017 | - Se pueden pasar como argumentos de otras funciones 1018 | - Se pueden utilizar en expresiones 1019 | - Se pueden incluir en varias estructuras de datos (como listas, tuplas, diccionarios, etc.) 1020 | 1021 | ### Argumentos de otras funciones 1022 | 1023 | Hasta ahora hemos visto que las funciones pueden recibir parámetros para realizar los cómputos que definen. Algunos de los tipos que hemos pasado son tipos simples como cadenas, números, listas, etc. Sin embargo, también pueden recibir funciones para crear abstracciones más poderosas. Veamos un ejemplo: 1024 | 1025 | ```py 1026 | def multiplicar_por_dos(n): 1027 | return n * 2 1028 | 1029 | def sumar_dos(n): 1030 | return n + 2 1031 | 1032 | def aplicar_operacion(f, numeros): 1033 | resultados = [] 1034 | for numero in numeros: 1035 | resultado = f(numero) 1036 | resultados.append(resultado) 1037 | 1038 | >>> nums = [1, 2, 3] 1039 | >>> aplicar_operacion(multiplicar_por_dos, nums) 1040 | [2, 4, 6] 1041 | 1042 | >>> aplicar_operacion(sumar_dos, nums) 1043 | [3, 4, 5] 1044 | ``` 1045 | 1046 | ### Funciones en expresiones 1047 | 1048 | Una forma de definir una función en una expresión es utilizando el keyword lambda. lambda tiene la siguiente sintaxis: lambda : . 1049 | 1050 | Otro ejemplo interesante es que las funciones se pueden utilizar en una expresión directamente. Esto es posible porque como lo hemos platicado con anterioridad, en Python las variables son simplemente nombres que apuntan a un objeto (en este caso a una función). Por ejemplo: 1051 | 1052 | ```py 1053 | sumar = lambda x, y: x + y 1054 | 1055 | >>> sumar(2, 3) 1056 | 5 1057 | ``` 1058 | 1059 | ### Funciones en estructuras de datos 1060 | 1061 | Las funciones también se pueden incluir en diversas estructuras que las permiten almacenar. Por ejemplo, una lista puede guardar diversas funciones a aplicar o un diccionario las puede almacenar como valores. 1062 | 1063 | ```py 1064 | def aplicar_operaciones(num): 1065 | operaciones = [abs, float] 1066 | 1067 | resultado = [] 1068 | for operacion in operaciones: 1069 | resultado.append(operacion(num)) 1070 | 1071 | return resultado 1072 | 1073 | >>> aplicar_operaciones(-2) 1074 | [2, -2.0] 1075 | ``` 1076 | 1077 | Como pudimos ver, las funciones son objetos muy versátiles que nos permiten tratarlas de diversas maneras y que nos permiten añadir capas adicionales de abstracción a nuestro programa. 1078 | 1079 | # Tipos estructurados, mutabilidad y funciones de alto nivel 1080 | 1081 | ## Tuplas 1082 | 1083 | Las **tuplas** son secuencias inmutables _(no se pueden modificar)_ de objetos, que a diferencia de las _cadenas_ pueden contener cualquier tipo de _objetos_. Estas pueden utilizarse para devolver varios valores en una función. 1084 | 1085 | ```py 1086 | def coordenadas(): 1087 | 1088 | """En nuestra función devolveremos la tupla (5, 4).""" 1089 | return (5,4) 1090 | 1091 | """Si ejecutamos la función vamos a recibir la tupla.""" 1092 | >>> coordenadas() 1093 | (5,4) 1094 | 1095 | """También podemos "desempaquetar" la tupla que recibimos, 1096 | esto significa que podemos asignar cada valor que nos llega de la 1097 | tupla a variables que definamos.""" 1098 | >>> x, y = coordenadas() 1099 | 1100 | """Si imprimimos la primera variable desempaquetada veremos 1101 | el primer valor de la tupla.""" 1102 | >>> x 1103 | 5 1104 | 1105 | """Y muy parecido al ejemplo anterior, con nuestra 1106 | variable "y" tendremos el segundo valor.""" 1107 | >>> y 1108 | 4 1109 | ``` 1110 | 1111 | ## Rangos 1112 | 1113 | Los **rangos** representan una secuencia de _enteros_ y se escriben como **range(comienzo, fin, pasos)** _comienzo y pasos son argumentos opcionales_. Al igual que las cadenas y las tuplas, los rangos son _inmutables_. Los **rangos** son muy eficientes en uso de memoria y normalmente utilizados en _for loops_. 1114 | 1115 | ```py 1116 | """Creamos un rango del 0 al 5 (el ultimo numero no se incluye)""" 1117 | my_range = range(0, 5) 1118 | 1119 | """Si realizamos un for loop en nuestro rango 1120 | e imprimimos el valor de i veremos 1121 | que nos imprime desde el 0 al 4.""" 1122 | for i in my_range: 1123 | print(i) 1124 | 1125 | 0 1126 | 1 1127 | 2 1128 | 3 1129 | 4 1130 | 1131 | ######################################################## 1132 | 1133 | """Creamos un rango del 0 al 7, pero esta 1134 | vez ira saltando de 2 en 2.""" 1135 | my_other_range = range(0, 7, 2) 1136 | 1137 | """Si realizamos un for loop en nuestro rango 1138 | e imprimimos el valor de i veremos 1139 | que nos imprime desde el 0 al 6 1140 | saltando de 2 en 2.""" 1141 | for i in my_other_range: 1142 | print(i) 1143 | 1144 | 0 1145 | 2 1146 | 4 1147 | 6 1148 | 1149 | ######################################################## 1150 | 1151 | """Creamos un rango del 0 al 8""" 1152 | another_range = range(0, 8, 2) 1153 | 1154 | """Si realizamos un for loop en nuestro rango 1155 | e imprimimos el valor de i veremos 1156 | que nos imprime desde el 0 al 6 1157 | saltando de 2 en 2. El 8 no se imprime, ya que el último número del el rango no se incluye.""" 1158 | for i in my_other_range: 1159 | print(i) 1160 | 1161 | 0 1162 | 2 1163 | 4 1164 | 6 1165 | ``` 1166 | 1167 | ## Listas y mutabilidad 1168 | 1169 | Las **listas** son secuencias de objetos, pero a diferencia de las [tuplas](#Tuplas) y [rangos](#Rangos), **sí son mutables**. Es posible _iterar_ con ellas, y cuando _modificas_ una lista, pueden existir efectos secundarios _(side effects)_. 1170 | 1171 | Para modificar una lista podemos: 1172 | - Asignar vía índice (_my_lista[0] = 5_) 1173 | - Utilizar los métodos de la lista (_append, pop, remove, insert, etc._) 1174 | 1175 | ```py 1176 | """Vamos a generar nuestra primera lista""" 1177 | my_list = [1, 2, 3] 1178 | 1179 | """Para acceder al primer índice lo haremos de la siguiente forma""" 1180 | my_list[0] 1181 | 1 1182 | 1183 | ######################################################## 1184 | 1185 | """Si queremos utilizar la notación de slices (dividir) definimos los 1186 | índices en los que dividiremos nuestra lista.""" 1187 | 1188 | """Aquí llamaremos desde el 2do índice hasta el final.""" 1189 | my_list[1:] 1190 | [2, 3] 1191 | 1192 | ######################################################## 1193 | 1194 | """Para agregar un ítem a nuestra lista lo haremos con la función append""" 1195 | my_list.append(4) 1196 | 1197 | """Ahora la lista tendrá 4 objetos.""" 1198 | print(my_list) 1199 | [1, 2, 3, 4] 1200 | 1201 | ######################################################## 1202 | 1203 | """Para modificar un elemento podemos hacerlo 1204 | referenciando su índice""" 1205 | my_list[0] = 'a' 1206 | print(my_list) 1207 | ['a', 2, 3, 5] 1208 | 1209 | ######################################################## 1210 | 1211 | """El método pop eliminara el último elemento de nuestra lista""" 1212 | my_list.pop() 1213 | 4 1214 | 1215 | print(my_list) 1216 | ['a', 2, 3] 1217 | 1218 | ######################################################## 1219 | 1220 | """Cuando una variable hace referencia a una lista 1221 | significa que apunta al mismo espacio en memoria, 1222 | esto significa que si cambia la lista se vera reflejado 1223 | en todas sus referencias, esto es un side effect""" 1224 | 1225 | """Creamos la lista a""" 1226 | a = [1, 2, 3] 1227 | 1228 | """Creamos la lista b que hará referencia a la lista a""" 1229 | b = a 1230 | 1231 | """Si imprimimos las listas serán iguales""" 1232 | a 1233 | [1, 2, 3] 1234 | 1235 | b 1236 | [1, 2, 3] 1237 | 1238 | """Si agrego un objeto a la lista a también se 1239 | vera reflejado en b""" 1240 | a.append(4) 1241 | 1242 | a 1243 | [1, 2, 3, 4] 1244 | 1245 | b 1246 | [1, 2, 3, 4] 1247 | 1248 | """Por esto debes tener mucho ojo al modificar las listas.""" 1249 | ``` 1250 | 1251 | ### Clonación 1252 | 1253 | Casi siempre es mejor **clonar** una _lista_ en vez de mutarla, esto nos ayuda a disminuir el riesgo de pérdida de la información. Para **clonar** una _lista_ podemos utilizar rebanadas (slices) o la función **list.** 1254 | 1255 | ```py 1256 | """Crearemos una lista a""" 1257 | a = [1, 2, 3] 1258 | 1259 | """Con la variable b clonaremos la lista a""" 1260 | b = list(a) 1261 | 1262 | """Si removemos el último elemento de a 1263 | no se vera reflejado en b""" 1264 | a.pop() 1265 | 3 1266 | 1267 | """Veamos los elementos de a""" 1268 | a 1269 | [1,2] 1270 | 1271 | """Y los elementos de b""" 1272 | b 1273 | [1, 2, 3] 1274 | ``` 1275 | 1276 | ### List comprehension 1277 | 1278 | Es una forma concisa de aplicar operaciones a los valores de una secuencia. También se pueden aplicar condiciones para filtrar. 1279 | 1280 | ```py 1281 | """Vamos a crear una lista con una operacion de range""" 1282 | my_list = list(range(10)) 1283 | 1284 | 1285 | """Si revisamos que contiene veremos que tiene todos 1286 | los números desde el 0 al 9""" 1287 | my_list 1288 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1289 | 1290 | ########################################################### 1291 | 1292 | """Ahora aplicaremos un list comprehension en donde 1293 | vamos a multiplicar * 2 cada uno de los elementos""" 1294 | double = [i * 2 for i in my_list] 1295 | 1296 | 1297 | """Y si revisamos los elementos de la lista veremos""" 1298 | double 1299 | [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 1300 | 1301 | ########################################################### 1302 | 1303 | """Otro ejemplo de list comprehension puede ser 1304 | solo tomar los numeros pares de nuestra lista""" 1305 | pares = [i for i in my_list if i % 2 == 0] 1306 | 1307 | 1308 | """Y si revisamos los elementos de la lista veremos""" 1309 | pares 1310 | [0, 2, 4, 6, 8] 1311 | ``` 1312 | 1313 | ## Diccionarios 1314 | 1315 | Los **diccionarios** son como listas, pero en lugar de usar índices utilizan **llaves**. Estos **no tienen un orden interno**, son **mutables** y pueden **iterarse.** 1316 | 1317 | ```py 1318 | """Creamos un diccionario utilizando los símbolos {} y debemos 1319 | darle un nombre a cada llave""" 1320 | my_dict = { 1321 | 'nombre': 'Karl', 1322 | 'apellido': 'Behrens', 1323 | 'edad': 26 1324 | } 1325 | 1326 | 1327 | """Para acceder algún dato de nuestro diccionario simplemente 1328 | llamamos nuestro diccionario y la llave""" 1329 | my_dict['apellido'] 1330 | 'Behrens' 1331 | 1332 | ########################################################### 1333 | 1334 | """Si queremos acceder a una llave pero la llave no existe, 1335 | podemos definir que nos devuelva una respuesta predeterminada""" 1336 | my_dict.get('email', 30) 1337 | 30 1338 | 1339 | 1340 | """Si llamamos una llave válida tendremos su valor""" 1341 | my_dict.get('nombre', 30) 1342 | 'Karl' 1343 | 1344 | ########################################################### 1345 | 1346 | """Para reasignar un valor simplemente lo hacemos 1347 | referenciando su llave""" 1348 | 1349 | my_dict 1350 | {'nombre': 'Karl', 'apellido': 'Behrens', 'edad': 26} 1351 | 1352 | my_dict['edad'] = 30 1353 | 1354 | """Vemos que la el valor de edad cambio""" 1355 | 1356 | my_dict 1357 | {'nombre': 'Karl', 'apellido': 'Behrens', 'edad': 30} 1358 | 1359 | ########################################################### 1360 | 1361 | """Si queremos eliminar un elemento lo 1362 | haremos con el método del y referenciado su llave""" 1363 | 1364 | del my_dict['edad'] 1365 | 1366 | my_dict 1367 | {'nombre': 'Karl', 'apellido': 'Behrens'} 1368 | 1369 | ########################################################### 1370 | 1371 | """Para obtener las llaves de nuestro diccionario 1372 | realizamos un método for loop.""" 1373 | 1374 | for llave in my_dict.keys(): 1375 | print(llave) 1376 | 1377 | nombre 1378 | apellido 1379 | 1380 | 1381 | """Y para los valores es muy parecido""" 1382 | 1383 | for valor in my_dict.values(): 1384 | print(valor) 1385 | 1386 | Karl 1387 | Behrens 1388 | 1389 | 1390 | """También podemos combinar estos for loops""" 1391 | 1392 | for llave, valor in my_dict.items(): 1393 | print(llave, valor) 1394 | 1395 | nombre Karl 1396 | apellido Behrens 1397 | 1398 | ########################################################### 1399 | 1400 | """Si deseamos revisar si una llave existe en nuestro diccionario 1401 | lo hacemos con un in""" 1402 | 1403 | 'email' in my_dict 1404 | False 1405 | 1406 | 1407 | 'apellido' in my_dict 1408 | True 1409 | ``` 1410 | 1411 | # Pruebas y debbugging 1412 | 1413 | ## Pruebas de caja negra 1414 | 1415 | Las **pruebas de caja negra** se basan en la especificación de la función o el programa, aquí debemos probas sus **inputs** y validar los **outputs.** Se llama **caja negra** por que no necesitamos saber necesariamente los procesos internos del programa, solo contrastar sus resultados. 1416 | 1417 | Estos tipos de pruebas son muy importantes para 2 tipos de test: 1418 | 1419 | - **Unit testing:** se realizan pruebas a cada uno de los módulos para determinar su correcto funcionamiento. 1420 | 1421 | - **Integration testing:** es cuando vemos que todos los módulos funcionan entre sí. 1422 | 1423 | Es una **buena práctica** realizar los test **antes** de crear tus lineas de código, esto es por que cualquier cambio que se realice a futuro los _test_ estarán incorporados para determinar si los cambios cumplen lo esperado. 1424 | 1425 | En Python existe la posibilidad de realizar _test_ gracias a la librería **unittest.** Puede ser que el siguiente código no lo entiendas en su totalidad, pero en una próxima guía detallare mas el tema de _clases_ en programación. Por ahora te mostrare como se realizan estos _test_. 1426 | 1427 | ```py 1428 | """Importamos la librería de unittest.""" 1429 | import unittest 1430 | 1431 | """Creamos una clase para los test, en este caso se llamara 1432 | CajaNegraTest, y como parámetro.""" 1433 | class CajaNegraTest(unittest.TestCase): 1434 | 1435 | """Definimos la función que generara el test.""" 1436 | def test_suma_dos_positivos(self): 1437 | 1438 | "Para nuestro ejemplo usaremos 2 parámetros.""" 1439 | num_1 = 10 1440 | num_2 = 5 1441 | 1442 | """Y dentro de la variable resultado 1443 | guardaremos lo que nos retornara la función suma.""" 1444 | resultado = suma(num_1, num_2) 1445 | 1446 | """Y para terminar definimos la variable resultado 1447 | y cual es el valor esperado.""" 1448 | self.assertEqual(resultado, 15) 1449 | 1450 | 1451 | """Para definir el módulo de Python escribimos lo siguiente.""" 1452 | if __name__ == '__main__': 1453 | unittest.main() 1454 | ``` 1455 | 1456 | Luego de escribir nuestro archivo iremos a la consola y ejecutaremos el _test_ 1457 | 1458 | ```bash 1459 | python3 caja_negra.py 1460 | 1461 | E # Obtenemos un error en nuestro test 1462 | ====================================================================== 1463 | ERROR: test_suma_dos_positivos (__main__.CajaNegraTest) # Veremos aqui el test con error 1464 | ---------------------------------------------------------------------- 1465 | Traceback (most recent call last): 1466 | File "caja_negra.py", line 9, in test_suma_dos_positivos 1467 | resultado = suma(num_1, num_2) 1468 | NameError: name 'suma' is not defined # La función suma no esta definida 1469 | 1470 | ---------------------------------------------------------------------- 1471 | Ran 1 test in 0.000s 1472 | 1473 | FAILED (errors=1) 1474 | ``` 1475 | 1476 | Como vimos en el recuadro anterior no definimos la función suma, para ello vamos a crearla. 1477 | 1478 | ```py 1479 | import unittest 1480 | 1481 | def suma(num_1, num_2): 1482 | return num_1 + num_2 1483 | 1484 | class CajaNegraTest(unittest.TestCase): 1485 | 1486 | def test_suma_dos_positivos(self): 1487 | num_1 = 10 1488 | num_2 = 5 1489 | 1490 | resultado = suma(num_1, num_2) 1491 | 1492 | self.assertEqual(resultado, 15) 1493 | 1494 | if __name__ == '__main__': 1495 | unittest.main() 1496 | ``` 1497 | 1498 | Ahora ejecutaremos de nuevo nuestro _test_ en la terminal. 1499 | 1500 | ```bash 1501 | python3 caja_negra.py 1502 | . 1503 | ---------------------------------------------------------------------- 1504 | Ran 1 test in 0.000s 1505 | 1506 | OK 1507 | ``` 1508 | 1509 | ## Pruebas de caja de cristal 1510 | 1511 | Se basan en el flujo del programa, por lo que se asume que conocemos el funcionamiento del programa, por lo que podemos probar todos los caminos posibles de una función. Esto significa que vamos a probar las ramificaciones, bucles for y while, recursiónes, etc. 1512 | 1513 | Este tipo de pruebas son muy buenas cuando debemos realizar: 1514 | 1515 | - **Regression testing o mocks:** descubrimos un **bug** cuando corremos el programa, por lo que vamos a buscar el **bug** gracias a que conocemos como esta estructurado el código. 1516 | 1517 | ## Debugging 1518 | 1519 | Los **bugs** son un problema que les sucede a todos, sin embargo si realizamos _test_ a nuestro programa probablemente tendremos menos **bugs**, pero esto no es suficiente. 1520 | 1521 | Existen unas **reglas generales** que nos ayudaran: 1522 | 1523 | - No te molestes con el debugger. Aprende a utilizar el print statement. 1524 | - Estudia los datos disponibles. 1525 | - Utiliza los datos para crear hipótesis y experimentos. Método científico. 1526 | - Ten una mente abierta. Si entendieras el programa, probablemente no habría bugs. 1527 | - Lleva un registro de lo que has tratado, preferentemente en la forma de tests. 1528 | 1529 | **Debuguear** es un proceso de búsqueda de los **bugs**, por lo que al diseñar nuestros experimentos debemos acotar el espacio de búsqueda en cada prueba. Una forma ágil de **debugear** es utilizando una **búsqueda binaria con print statements**, esto significa que ejecutamos la mitad del código, si no falla entonces sabemos que el problema esta en la otra mitad, y en cada área que vamos acortando lo dividimos por mitades, de esta forma hallaremos rápidamente nuestro **bug**. 1530 | 1531 | Existe un listado de **errores comunes** de los cuales también nos podemos apoyar: 1532 | 1533 | - Encuentra a los sospechosos comunes (llamado a una función mal escrita, parámetros en orden incorrecto, etc.) 1534 | - En lugar de preguntarte por qué un programa no funciona, pregúntate por qué está funcionando de esta manera. 1535 | - Es posible que el bug no se encuentre donde crees que está. 1536 | - Explícale el problema a otra persona. De preferencia que no tenga contexto. 1537 | - Lleva un registro de lo que has tratado, preferentemente en la forma de tests. 1538 | - Vete a dormir. 1539 | 1540 | # Excepciones y afirmaciones 1541 | 1542 | ## Manejo de excepciones 1543 | 1544 | Los **manejos de excepciones** son muy comunes en la programación, no tienen nada de excepcional. Las **excepciones** de Python normalmente se relacionan con errores de semántica, también podemos crear nuestras propias **excepciones**, pero cuando una **excepción** no se maneja (_unhandled exception_), el programa termina en error. 1545 | 1546 | Las **excepciones** se manejan con los keywords: **try, except, finally.** Se pueden utilizar también para _ramificar_ programas. 1547 | 1548 | No deben manejarse de manera silenciosa (por ejemplo, con print statements). Para crear tu propia excepción utiliza el keyword _raise_. 1549 | 1550 | ```py 1551 | """Creamos una función en donde cada elemento de 1552 | una lista es dividida por un divisor definido""" 1553 | def divide_elementos_de_lista(lista, divisor): 1554 | """El programa intentara realizar la división""" 1555 | try: 1556 | return [i / divisor for i in lista] 1557 | 1558 | """En caso de error de tipo ZeroDivisionError que 1559 | significa error al dividir en cero, el programa 1560 | ejecutara la siguiente instrucción""" 1561 | except ZeroDivisionError as e: 1562 | return lista 1563 | 1564 | lista = list(range(10)) 1565 | divisor = 0 1566 | 1567 | print(divide_elementos_de_lista(lista, divisor)) 1568 | ``` 1569 | 1570 | ## Excepciones como control de flujo 1571 | 1572 | Hasta ahora hemos visto como las excepciones nos permiten controlar los posibles errores que pueden ocurrir en nuestro código. Sin embargo, dentro de la comunidad de Python tienen otro uso: control de flujo. 1573 | 1574 | En este momento ya debes estar familiarizado con las estructuras de control flujo que ofrece Python ```(if... elif...else)```; entonces, ¿por qué es necesaria otra modalidad para controlar el flujo? Una razón muy específica: el principio EAFP (easier to ask for forgiveness than permission, es más fácil pedir perdón que permiso, por sus siglas en inglés). 1575 | 1576 | El principio EAFP es un estilo de programación común en Python en el cual se asumen llaves, índices o atributos válidos y se captura la excepción si la suposición resulta ser falsa. Es importante resaltar que otros lenguajes de programación favorecen el principio LBYL (look before you leap, revisa antes de saltar) en el cual el código verifica de manera explícita las precondiciones antes de realizar llamadas. 1577 | 1578 | Veamos ambos estilos: 1579 | 1580 | ```py 1581 | # Python 1582 | 1583 | def busca_pais(paises, pais): 1584 | """ 1585 | Países es un diccionario. País es la llave. 1586 | Codigo con el principio EAFP. 1587 | """ 1588 | 1589 | try: 1590 | return paises[pais] 1591 | except KeyError: 1592 | return None 1593 | ``` 1594 | 1595 | ```js 1596 | // Javascript 1597 | 1598 | /** 1599 | * Paises es un objeto. Pais es la llave. 1600 | * Codigo con el principio LBYL. 1601 | */ 1602 | function buscaPais(paises, pais) { 1603 | if(!Object.keys(paises).includes(pais)) { 1604 | return null; 1605 | } 1606 | 1607 | return paises[pais]; 1608 | } 1609 | ``` 1610 | 1611 | Como puedes ver, el código de Python accede directamente a la llave y únicamente si dicho acceso falla, entonces se captura la excepción y se provee el código necesario. En el caso de JavaScript, se verifica primero que la llave exista en el objeto y únicamente con posterioridad se accede. 1612 | 1613 | Es importante resaltar que ambos estilos pueden utilizarse en Python, pero el estilo EAFP es mucho más “pythonico”. 1614 | 1615 | ## Afirmaciones 1616 | 1617 | Las **afirmaciones** son un mecanismo en la que podemos determinar si una afirmación se cumple o no se cumple y poder seguir adelante con la ejecución de nuestro programa o darle término. 1618 | 1619 | Las **afirmaciones** es un método de programación defensiva, esto significa que nos estamos preparando para verificar que los tipos de _inputs_ de nuestro programa es del tipo que nosotros esperamos. Estos también nos sirven para debuggear. 1620 | 1621 | Para realizar una afirmación en nuestro programa lo hacemos con la expresión ```assert , ```. 1622 | 1623 | ```py 1624 | def primera_letra(lista_de_palabras): 1625 | primeras_letras = [] 1626 | 1627 | for palabra in lista_de_palabras: 1628 | assert type(palabra) == str, f'{palabra} no es str' 1629 | assert len(palabra) > 0, 'No se permiten str vacíos' 1630 | 1631 | primeras_letras.append(palabra[0]) 1632 | 1633 | return primeras_letras 1634 | ``` 1635 | -------------------------------------------------------------------------------- /aproximacion.py: -------------------------------------------------------------------------------- 1 | objetivo = int(input('Escoge un numero: ')) 2 | 3 | epsilon = 0.01 4 | paso = epsilon**2 5 | respuesta = 0 6 | 7 | while abs(respuesta**2 - objetivo) >= epsilon and respuesta <= objetivo: 8 | respuesta += paso 9 | 10 | if abs(respuesta**2 - objetivo) >= epsilon: 11 | print(f'No se encontró la raiz cuadrada de {objetivo}') 12 | else: 13 | print(f'La raiz cuadrada de {objetivo} es {respuesta}') -------------------------------------------------------------------------------- /busqueda_binaria.py: -------------------------------------------------------------------------------- 1 | objetivo = int(input('Escoge un numero: ')) 2 | epsilon = 0.01 3 | bajo = 0.0 4 | alto = max(1.0, objetivo) 5 | respuesta = (alto + bajo) / 2 6 | 7 | while abs(respuesta**2 - objetivo) >= epsilon: 8 | if respuesta**2 < objetivo: 9 | bajo = respuesta 10 | else: 11 | alto = respuesta 12 | 13 | respuesta = (alto + bajo) / 2 14 | 15 | print(f'La raiz cuadrada de {objetivo} es {respuesta}') -------------------------------------------------------------------------------- /caja_negra.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | def suma(num_1, num_2): 4 | return num_1 + num_2 5 | 6 | class CajaNegraTest(unittest.TestCase): 7 | 8 | def test_suma_dos_positivos(self): 9 | num_1 = 10 10 | num_2 = 5 11 | 12 | resultado = suma(num_1, num_2) 13 | 14 | self.assertEqual(resultado, 15) 15 | 16 | if __name__ == '__main__': 17 | unittest.main() 18 | 19 | -------------------------------------------------------------------------------- /enumeracion.py: -------------------------------------------------------------------------------- 1 | objetivo = int(input('Escoge un entero: ')) 2 | respuesta = 0 3 | 4 | while respuesta**2 < objetivo: 5 | respuesta += 1 6 | 7 | if respuesta**2 == objetivo: 8 | print(f'La raiz cuadrada de {objetivo} es {respuesta}') 9 | 10 | else: 11 | print(f'{objetivo} no tinene una raiz cuadrada exacta') -------------------------------------------------------------------------------- /factoriales.py: -------------------------------------------------------------------------------- 1 | def factorial(n): 2 | """Calcula el factorial de n 3 | 4 | n int > 0 5 | return n! 6 | """ 7 | 8 | if n == 1: 9 | return 1 10 | 11 | return n * factorial(n - 1) 12 | 13 | n = int(input('Escribe un entero: ')) 14 | 15 | print(factorial(n)) 16 | -------------------------------------------------------------------------------- /iteraciones.py: -------------------------------------------------------------------------------- 1 | 2 | contador = 0 3 | 4 | while contador < 10: 5 | print(contador) 6 | contador += 1 # contador = contador + 1 7 | 8 | if contador > 6: 9 | break -------------------------------------------------------------------------------- /programas_ramificados.py: -------------------------------------------------------------------------------- 1 | num_1 = int(input('Escoge un entero: ')) 2 | num_2 = int(input('Escoge otro entero: ')) 3 | 4 | if num_1 > num_2: 5 | print('El primer numero es mayor que el segundo.') 6 | elif num_1 < num_2: 7 | print('El segundo numero es mayor que el primero.') 8 | else: 9 | print('Los 2 numeros son iguales.') 10 | -------------------------------------------------------------------------------- /readme_img/Grace-Murray-Hopper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/Grace-Murray-Hopper.jpg -------------------------------------------------------------------------------- /readme_img/Telar-de-Jacquard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/Telar-de-Jacquard.jpg -------------------------------------------------------------------------------- /readme_img/alan-turing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/alan-turing.jpg -------------------------------------------------------------------------------- /readme_img/arquitectura-neumann.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/arquitectura-neumann.png -------------------------------------------------------------------------------- /readme_img/busqueda-binaria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/busqueda-binaria.png -------------------------------------------------------------------------------- /readme_img/church_alonzo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/church_alonzo.jpg -------------------------------------------------------------------------------- /readme_img/computador-cuantico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/computador-cuantico.png -------------------------------------------------------------------------------- /readme_img/computadora-griega.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/computadora-griega.jpg -------------------------------------------------------------------------------- /readme_img/dennis-ritchie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/dennis-ritchie.jpg -------------------------------------------------------------------------------- /readme_img/eniac.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/eniac.jpg -------------------------------------------------------------------------------- /readme_img/feynman.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/feynman.jpeg -------------------------------------------------------------------------------- /readme_img/lovelace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/lovelace.jpg -------------------------------------------------------------------------------- /readme_img/maquina-tabuladora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/maquina-tabuladora.jpg -------------------------------------------------------------------------------- /readme_img/microchips.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/microchips.jpg -------------------------------------------------------------------------------- /readme_img/motor-babbage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/motor-babbage.jpg -------------------------------------------------------------------------------- /readme_img/neumann-edvac.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/neumann-edvac.jpeg -------------------------------------------------------------------------------- /readme_img/nube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/nube.jpg -------------------------------------------------------------------------------- /readme_img/oblea-silicio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/oblea-silicio.jpg -------------------------------------------------------------------------------- /readme_img/operador-logico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/operador-logico.png -------------------------------------------------------------------------------- /readme_img/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/python.png -------------------------------------------------------------------------------- /readme_img/reserved-words-python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/reserved-words-python.png -------------------------------------------------------------------------------- /readme_img/van-rossum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/35bytes/introduccion-pensamiento-computacional/3f1cd3bcbea4e835e5b136461d589de2bc6bd96b/readme_img/van-rossum.jpg --------------------------------------------------------------------------------