|
| 1 | +# Introducción |
| 2 | + |
| 3 | +El uso de una función de Python es el primer paso para codificar después de las estructuras de datos y los condicionales básicos. Las funciones permiten la reutilización, lo que evita la duplicación de código. Cuando los proyectos reutilizan código con funciones, se vuelven más legibles y fáciles de mantener. |
| 4 | + |
| 5 | +## Escenario: Organización de datos sobre un cohete |
| 6 | + |
| 7 | +Imagina que vas a crear un programa para construir información precisa sobre un cohete espacial. Las funciones reutilizables te permitirán no solo calcular información, sino también crear valores combinando entradas y salidas de otras funciones. |
| 8 | + |
| 9 | +## ¿Qué descubrirás? |
| 10 | +En este módulo, aprenderás a: |
| 11 | + |
| 12 | +* Trabajar con entradas predeterminadas, obligatorias y de carácter comodín. |
| 13 | +* Hacer que el código sea reutilizable extrayendo patrones comunes en funciones independientes. |
| 14 | +* Devolver valores, estructuras de datos o resultados calculados. |
| 15 | + |
| 16 | +## ¿Cuál es el objetivo principal? |
| 17 | +Al final de este módulo, comprenderás algunas de las reglas y el comportamiento asociado a las funciones, incluida la forma de controlar las entradas y salidas. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## Aspectos básicos de las funciones de Python |
| 22 | + |
| 23 | +Las funciones son el siguiente paso después de haber aprendido los conceptos básicos de programación de Python. En su forma más sencilla, una función contiene código que siempre devuelve un valor (o valores). En algunos casos, una función también tiene entradas opcionales u obligatorias. |
| 24 | + |
| 25 | +Al empezar a escribir código que duplica otras partes del programa, se convierte en una oportunidad perfecta para extraer el código en una función. Aunque compartir código común mediante funciones es útil, también se puede limitar el tamaño del código extrayendo partes en funciones más pequeñas (y legibles). |
| 26 | + |
| 27 | +Los programas que evitan la duplicación y evitan funciones de gran tamaño mediante funciones más pequeñas son más legibles y fáciles de mantener. También son más fáciles de depurar cuando las cosas no funcionan correctamente. |
| 28 | + |
| 29 | +Hay varias reglas sobre las entradas de funciones que son fundamentales para aprovechar al máximo todo lo que las funciones tienen que ofrecer. |
| 30 | + |
| 31 | +*Aunque se usa el término entrada para describir las funciones que se aceptan, estos elementos normalmente se denominan argumentos y/o parámetros. Para mantener la coherencia en este módulo, a las entradas las denominaremos argumentos.* |
| 32 | + |
| 33 | +### Funciones sin argumentos |
| 34 | +Para crear una función, utilizamos la palabra clave `def`, seguida de un nombre, paréntesis y, después, del cuerpo con el código de función: |
| 35 | + |
| 36 | +``` |
| 37 | +# Defino mi función |
| 38 | +def rocket_parts(): |
| 39 | + print('payload, propellant, structure') |
| 40 | +``` |
| 41 | + |
| 42 | +En este caso, rocket_parts es el nombre de la función. Ese nombre va seguido de paréntesis vacíos, que indican que no se necesitan argumentos. El último es el código, con sangría de cuatro espacios. Para trabajar con la función, debes llamarla por su nombre usando paréntesis: |
| 43 | + |
| 44 | +``` |
| 45 | +# Llamo a mi función |
| 46 | +
|
| 47 | +>>> rocket_parts() |
| 48 | +'payload, propellant, structure' |
| 49 | +``` |
| 50 | + |
| 51 | +La función rocket_parts() no toma ningún argumento e imprime una instrucción sobre la gravedad. Si necesitas usar un valor que devuelve una función, puedes asignar la salida de la función a una variable: |
| 52 | + |
| 53 | +``` |
| 54 | +>>> output = rocket_parts() |
| 55 | +payload, propellant, structure |
| 56 | +
|
| 57 | +>>> output is None |
| 58 | +True |
| 59 | +``` |
| 60 | + |
| 61 | +Puede parecer sorprendente que el valor de la variable output sea None. Esto se debe a que la función rocket_parts() no ha devuelto explícitamente un valor. En Python, si una función no devuelve explícitamente un valor, devuelve implícitamente None. Actualizar la función para devolver la cadena en lugar de imprimirla hace que la variable output tenga un valor distinto: |
| 62 | + |
| 63 | +``` |
| 64 | +>>> def rocket_parts(): |
| 65 | +... return 'payload, propellant, structure' |
| 66 | +... |
| 67 | +>>> output = rocket_parts() |
| 68 | +>>> output |
| 69 | +'payload, propellant, structure' |
| 70 | +``` |
| 71 | +Si necesitas usar el valor de una función, esa función debe devolver el valor explícitamente. De lo contrario; se devolverá `None`. |
| 72 | + |
| 73 | +*No es necesario asignar siempre la devolución de una función. En la mayoría de los casos en los que una función no devuelve un valor (o valores) explícitamente, significa que no es necesario asignar ni usar el valor implícito `None` que se devuelve.* |
| 74 | + |
| 75 | +### Argumentos opcionales y requeridos |
| 76 | +En Python, varias funciones integradas requieren argumentos. Algunas funciones integradas hacen que los argumentos sean opcionales. Las funciones integradas están disponibles de inmediato, por lo que no es necesario importarlas explícitamente. |
| 77 | + |
| 78 | +Un ejemplo de una función integrada que requiere un argumento es `any()`. Esta función toma un objeto iterable (por ejemplo, una lista) y devuelve `True` si algún elemento del objeto iterable es `True`. De lo contrario, devuelve `False`. |
| 79 | + |
| 80 | +``` |
| 81 | +>>> any([True, False, False]) |
| 82 | +True |
| 83 | +>>> any([False, False, False]) |
| 84 | +False |
| 85 | +``` |
| 86 | + |
| 87 | +Si llamamos a `any()` sin ningún argumento, se genera una excepción útil. El mensaje de error explica que necesita al menos un argumento: |
| 88 | + |
| 89 | +``` |
| 90 | +>>> any() |
| 91 | +Traceback (most recent call last): |
| 92 | + File '<stdin>', line 1, in <module> |
| 93 | +TypeError: any() takes exactly one argument (0 given) |
| 94 | +``` |
| 95 | + |
| 96 | +Puedes comprobar que algunas funciones permiten el uso de argumentos opcionales mediante otra función integrada denominada `str()`. Esta función crea una cadena a partir de un argumento. Si no se pasa ningún argumento, devuelve una cadena vacía: |
| 97 | + |
| 98 | +``` |
| 99 | +>>> str() |
| 100 | +'' |
| 101 | +>>> str(15) |
| 102 | +'15' |
| 103 | +``` |
| 104 | + |
| 105 | +## Uso de argumentos en una función de Python |
| 106 | + |
| 107 | +Ahora que sabes cómo crear una función sin entradas, el paso siguiente es crear funciones que requieran un argumento. El uso de argumentos hace que las funciones sean más flexibles, ya que pueden hacer más y condicionalizar lo que hacen. |
| 108 | + |
| 109 | +### Exigencia de un argumento |
| 110 | +Si vas a pilotar un cohete, una función sin entradas obligatorias es como un equipo con un botón que le indique la hora. Si presionas el botón, una voz computarizada le indicará la hora. Pero una entrada necesaria puede ser un destino para calcular la distancia del viaje. Las entradas obligatorias se denominan *argumentos* para la función. |
| 111 | + |
| 112 | +Para requerir un argumento, agrégalo entre paréntesis: |
| 113 | + |
| 114 | +``` |
| 115 | +def distance_from_earth(destination): |
| 116 | + if destination == 'Moon': |
| 117 | + return '238,855' |
| 118 | + else: |
| 119 | + return 'Unable to compute to that destination' |
| 120 | +``` |
| 121 | +Intenta llamar a la función distance_from_earth() sin argumento alguno: |
| 122 | + |
| 123 | +``` |
| 124 | +>>> distance_from_earth() |
| 125 | +Traceback (most recent call last): |
| 126 | + File '<stdin>', line 1, in <module> |
| 127 | +TypeError: distance_from_earth() missing 1 required positional argument: 'destination' |
| 128 | +``` |
| 129 | + |
| 130 | +Python genera `TypeError` con un mensaje de error que indica que la función requiere un argumento denominado destination. Si se pide al equipo del cohete que calcule la distancia del viaje con un destino, debes solicitar que un destino es un requisito. El código de ejemplo tiene dos rutas de acceso para una respuesta, una para la Luna y la otra para cualquier otra cosa. Use la Luna como entrada para obtener una respuesta: |
| 131 | + |
| 132 | +``` |
| 133 | +>>> distance_from_earth('Moon') |
| 134 | +'238,855' |
| 135 | +``` |
| 136 | + |
| 137 | +Dado que hay una condición catch-all, intenta usar cualquier otra cadena como destino para comprobar ese comportamiento: |
| 138 | + |
| 139 | +``` |
| 140 | +>>> distance_from_earth('Saturn') |
| 141 | +'Unable to compute to that destination' |
| 142 | +``` |
| 143 | + |
| 144 | +### Varios argumentos necesarios |
| 145 | +Para usar varios argumentos, debes separarlos con una coma. Vamos a crear una función que pueda calcular cuántos días se tardarán en llegar a un destino, dadas la distancia y una velocidad constante: |
| 146 | + |
| 147 | +``` |
| 148 | +def days_to_complete(distance, speed): |
| 149 | + hours = distance/speed |
| 150 | + return hours/24 |
| 151 | +``` |
| 152 | + |
| 153 | +Ahora usa la distancia entre la Tierra y la Luna para calcular cuántos días tardaría en llegar a la Luna con un límite de velocidad común de 120 kilómetros por hora: |
| 154 | + |
| 155 | +``` |
| 156 | +>>> days_to_complete(238855, 75) |
| 157 | +132.69722222222222 |
| 158 | +``` |
| 159 | +### Funciones como argumentos |
| 160 | +Puedes usar el valor de la función days_to_complete() y asignarlo a una variable y, después, pasarlo a round() (una función integrada que redondea al número entero más cercano) para obtener un número entero: |
| 161 | + |
| 162 | +``` |
| 163 | +>>> total_days = days_to_complete(238855, 75) |
| 164 | +>>> round(total_days) |
| 165 | +133 |
| 166 | +``` |
| 167 | +Pero un patrón útil es pasar funciones a otras funciones en lugar de asignar el valor devuelto: |
| 168 | + |
| 169 | +``` |
| 170 | +>>> round(days_to_complete(238855, 75)) |
| 171 | +133 |
| 172 | +``` |
| 173 | + |
| 174 | +**Sugerencia** |
| 175 | + |
| 176 | +Aunque pasar funciones directamente a otras funciones como entrada es útil, existe la posibilidad de que se reduzca la legibilidad. Este patrón es especialmente problemático cuando las funciones requieren muchos argumentos. |
| 177 | + |
| 178 | +## Uso de argumentos de palabra clave en Python |
| 179 | + |
| 180 | +Los argumentos opcionales requieren un valor predeterminado asignado a ellos. Estos argumentos con nombre se denominan *argumentos de palabra clave*. Los valores del argumento de palabra clave deben definirse en las propias funciones. Cuando se llama a una función definida con argumentos de palabra clave, no es necesario usarlos en absoluto. |
| 181 | + |
| 182 | +La misión Apolo 11 tardó unas 51 horas en llegar a la Luna. Vamos a crear una función que devuelva la hora estimada de llegada usando el mismo valor que la misión Apolo 11 como valor predeterminado: |
| 183 | + |
| 184 | +``` |
| 185 | +from datetime import timedelta, datetime |
| 186 | +
|
| 187 | +def arrival_time(hours=51): |
| 188 | + now = datetime.now() |
| 189 | + arrival = now + timedelta(hours=hours) |
| 190 | + return arrival.strftime('Arrival: %A %H:%M') |
| 191 | +``` |
| 192 | + |
| 193 | +La función usa el módulo `datetime` para definir la hora actual. Usa `timedelta` para permitir la operación de suma que da como resultado un objeto de hora nuevo. Después de calcular ese resultado, devuelve la estimación `arrival` con formato de cadena. Intentando llamarla sin algún argumento: |
| 194 | + |
| 195 | +``` |
| 196 | +>>> arrival_time() |
| 197 | +'Arrival: Saturday 16:42' |
| 198 | +``` |
| 199 | + |
| 200 | +Aunque la función define un argumento de palabra clave, no permite pasar uno cuando se llama a una función. En este caso, la variable `hours` tiene como valor predeterminado `51`. Para comprobar que la fecha actual es correcta, usamos `0` como valor para `hours`: |
| 201 | + |
| 202 | +``` |
| 203 | +>>> arrival_time(hours=0) |
| 204 | +'Arrival: Thursday 13:42' |
| 205 | +``` |
| 206 | + |
| 207 | +### Combinación de argumentos y argumentos de palabra clave |
| 208 | + |
| 209 | +A veces, una función necesita una combinación de argumentos de palabra clave y argumentos. En Python, esta combinación sigue un orden específico. Los argumentos siempre se declaran primero, seguidos de argumentos de palabra clave. |
| 210 | + |
| 211 | +Actualizando la función `arrival_time()` para que tome un argumento necesario, que es el nombre del destino: |
| 212 | + |
| 213 | +``` |
| 214 | +from datetime import timedelta, datetime |
| 215 | +
|
| 216 | +def arrival_time(destination, hours=51): |
| 217 | + now = datetime.now() |
| 218 | + arrival = now + timedelta(hours=hours) |
| 219 | + return arrival.strftime(f'{destination} Arrival: %A %H:%M') |
| 220 | +``` |
| 221 | + |
| 222 | +Dado que hemos agregado un argumento necesario, ya no es posible llamar a la función sin ningún argumento: |
| 223 | + |
| 224 | + |
| 225 | +``` |
| 226 | +>>> arrival_time() |
| 227 | +Traceback (most recent call last): |
| 228 | + File '<stdin>', line 1, in <module> |
| 229 | +TypeError: arrival_time() missing 1 required positional argument: 'destination' |
| 230 | +``` |
| 231 | +Usamos 'Moon' como valor para destination a fin de evitar el error: |
| 232 | + |
| 233 | +``` |
| 234 | +>>> arrival_time('Moon') |
| 235 | +'Moon Arrival: Saturday 16:54' |
| 236 | +``` |
| 237 | +También podemos pasar más de dos valores, pero debemos separarlos con una coma. Se tarda aproximadamente 8 minutos (0,13 horas) en entrar en órbita, así que utilizaremos eso como argumento: |
| 238 | + |
| 239 | +``` |
| 240 | +>>> arrival_time('Orbit', hours=0.13) |
| 241 | +'Orbit Arrival: Thursday 14:11' |
| 242 | +``` |
| 243 | + |
| 244 | +## Uso de argumentos de variable en Python |
| 245 | + |
| 246 | +En Python, puedes usar cualquier número de argumentos de palabra clave y argumentos sin necesidad de declarar cada uno de ellos. Esta capacidad es útil cuando una función puede obtener un número desconocido de entradas. |
| 247 | + |
| 248 | +### Argumentos de variable |
| 249 | +Los argumentos en las funciones son necesarios. Pero cuando se usan argumentos de variable, la función permite pasar cualquier número de argumentos (incluido `0`). La sintaxis para usar argumentos de variable es agregar un asterisco único como prefijo (`*`) antes del nombre del argumento. |
| 250 | + |
| 251 | +La función siguiente imprime los argumentos recibidos: |
| 252 | + |
| 253 | +``` |
| 254 | +def variable_length(*args): |
| 255 | + print(args) |
| 256 | +``` |
| 257 | +*No es necesario denominar a los argumentos de variable `args`. Puedes usar cualquier nombre de variable válido. Aunque es habitual ver *args o *a, debe intentar usar la misma convención en un proyecto.* |
| 258 | + |
| 259 | +En este caso, `*args` indica a la función que acepta cualquier número de argumentos (incluido `0`). En la función, `args` ahora está disponible como la variable que contiene todos los argumentos como una tupla. Pruebe la función pasando cualquier número o tipo de argumentos: |
| 260 | + |
| 261 | +``` |
| 262 | +>>> variable_length() |
| 263 | +() |
| 264 | +>>> variable_length('one', 'two') |
| 265 | +('one', 'two') |
| 266 | +>>> variable_length(None) |
| 267 | +(None,) |
| 268 | +``` |
| 269 | +Como puedes ver, no hay ninguna restricción en el número o tipo de argumentos que se pasan. |
| 270 | + |
| 271 | +Un cohete realiza varios pasos antes de un lanzamiento. En función de las tareas o retrasos, estos pasos pueden tardar más de lo previsto. Vamos a crear una función de longitud variable que pueda calcular cuántos minutos quedan hasta el inicio, dado el tiempo que va a tardar cada paso: |
| 272 | + |
| 273 | +``` |
| 274 | +def sequence_time(*args): |
| 275 | + total_minutes = sum(args) |
| 276 | + if total_minutes < 60: |
| 277 | + return f'Total time to launch is {total_minutes} minutes' |
| 278 | + else: |
| 279 | + return f'Total time to launch is {total_minutes/60} hours' |
| 280 | +``` |
| 281 | +Probamos la función pasando cualquier número de minutos: |
| 282 | + |
| 283 | +``` |
| 284 | +>>> sequence_time(4, 14, 18) |
| 285 | +'Total time to launch is 36 minutes' |
| 286 | +>>> sequence_time(4, 14, 48) |
| 287 | +'Total time to launch is 1.1 hours' |
| 288 | +``` |
| 289 | +*Cuando se utilizan argumentos de variable, a cada valor ya no se le asigna un nombre de variable. Todos los valores ahora forman parte del nombre de variable catch-all que usa el asterisco (en estos ejemplos, args).* |
| 290 | + |
| 291 | +### Argumentos de palabra clave variable |
| 292 | +Para que una función acepte cualquier número de argumentos de palabra clave, debe usar una sintaxis similar. En este caso, se requiere un asterisco doble: |
| 293 | + |
| 294 | +``` |
| 295 | +def variable_length(**kwargs): |
| 296 | + print(kwargs) |
| 297 | +``` |
| 298 | + |
| 299 | +Prueba la función de ejemplo, que imprime los nombres y valores pasados como `kwargs`: |
| 300 | +``` |
| 301 | +>>> variable_length(tanks=1, day='Wednesday', pilots=3) |
| 302 | +{'tanks': 1, 'day': 'Wednesday', 'pilots': 3} |
| 303 | +``` |
| 304 | + |
| 305 | +Si ya conoces bien los diccionarios de Python, observarás que los argumentos de palabra clave de longitud variable se asignan como un diccionario. Para interactuar con las variables y los valores, usamos las mismas operaciones que un diccionario. |
| 306 | + |
| 307 | +*Al igual que con los argumentos de variable, no es necesario usar kwargs cuando se usan argumentos de palabra clave variable. Puede usar cualquier nombre de variable válido. Aunque es habitual ver **kwargs o **kw, debe intentar usar la misma convención en un proyecto.* |
| 308 | + |
| 309 | +En esta función, vamos a usar argumentos de palabra clave variable para notificar los astronautas asignados a la misión. Dado que esta función permite cualquier número de argumentos de palabra clave, se puede reutilizar independientemente del número de astronautas asignados: |
| 310 | + |
| 311 | +``` |
| 312 | +def crew_members(**kwargs): |
| 313 | + print(f'{len(kwargs)} astronauts assigned for this mission:') |
| 314 | + for title, name in kwargs.items(): |
| 315 | + print(f'{title}: {name}') |
| 316 | +``` |
| 317 | + |
| 318 | +Probando con la tripulación del Apolo 11: |
| 319 | +``` |
| 320 | +>>> crew_members(captain='Neil Armstrong', pilot='Buzz Aldrin', command_pilot='Michael Collins') |
| 321 | +3 astronauts assigned for this mission: |
| 322 | +captain: Neil Armstrong |
| 323 | +pilot: Buzz Aldrin |
| 324 | +command_pilot: Michael Collins |
| 325 | +``` |
| 326 | + |
| 327 | +Dado que puede pasar cualquier combinación de argumentos de palabra clave, nos aseguramos de evitar palabras clave repetidas. Las palabras clave repetidas producirán un error: |
| 328 | + |
| 329 | +``` |
| 330 | +>>> crew_members(captain='Neil Armstrong', pilot='Buzz Aldrin', pilot='Michael Collins') |
| 331 | + File '<stdin>', line 1 |
| 332 | +SyntaxError: keyword argument repeated: pilot |
| 333 | +``` |
| 334 | + |
| 335 | +--- |
| 336 | +## Resumen |
| 337 | + |
| 338 | +Las funciones de Python permiten escribir código modular y reutilizable, calcular valores y realizar acciones basadas en el contexto. En este módulo, ha obtenido información sobre algunas de las reglas y el comportamiento asociados a las funciones. Estas reglas incluían enfrentarse a entradas y salidas. Por último, ha tenido la oportunidad de extraer y mejorar el código para que sea más legible y fácil de mantener. |
| 339 | + |
| 340 | +Ahora conoces aspectos esenciales como estos sobre las funciones: |
| 341 | + |
| 342 | +* Las funciones pueden requerir argumentos o convertirlos en opcionales. |
| 343 | +* Puedes extraer código reutilizable y reutilizarlo en una función. |
| 344 | +* Los argumentos de variable y los argumentos de palabra clave de variable son útiles cuando no se conocen las entradas exactas. |
| 345 | +* Con estas técnicas y el conocimiento de las funciones, deberías sentirse más cómodo abordando problemas más grandes al escribir código de Python. |
| 346 | + |
| 347 | +--- |
| 348 | + |
| 349 | +Curso Propedútico de Python para Launch X - Innovacción Virtual. |
| 350 | + |
| 351 | +Material desarrollado con base en los contenidos de MSLearn y la metáfora de LaunchX, traducción e implementación por: Fernanda Ochoa - Learning Producer de LaunchX. |
| 352 | + |
| 353 | +Redes: |
| 354 | +* GitHub: [FernandaOchoa](https://github.com/FernandaOchoa) |
| 355 | +* Twitter: [@imonsh](https://twitter.com/imonsh) |
| 356 | +* Instagram: [fherz8a](https://www.instagram.com/fherz8a/) |
0 commit comments