Funciones de escritura
Última actualización: 2025-07-01 | Mejora esta página
Tiempo estimado: 25 minutos
Hoja de ruta
Preguntas
- ¿Cómo puedo crear mis propias funciones?
Objetivos
- Explica e identifica la diferencia entre definición de función y llamada a función.
- Escribe una función que tome un número pequeño y fijo de argumentos y produzca un único resultado.
Descomponer los programas en funciones para facilitar su comprensión.
- Los seres humanos sólo pueden mantener unos pocos elementos en la memoria de trabajo a la vez.
- Comprender ideas más grandes/más complicadas entendiendo y
combinando piezas.
- Componentes de una máquina.
- Lemas para demostrar teoremas.
- Las funciones tienen el mismo propósito en los programas.
- Encapsular la complejidad para que podamos tratarla como una única “cosa”.
- También permite reutilizar.
- Escribir una vez, usar muchas veces.
Define una función usando def
con un nombre, parámetros
y un bloque de código.
- Comienza la definición de una nueva función con
def
. - Seguido del nombre de la función.
- Debe obedecer las mismas reglas que los nombres de variables.
- Entonces parámetros entre paréntesis.
- Paréntesis vacíos si la función no toma ninguna entrada.
- Lo discutiremos en detalle dentro de un momento.
- Entonces dos puntos.
- A continuación, un bloque de código con sangría.
Definir una función no la ejecuta.
- Definir una función no la ejecuta.
- Como asignar un valor a una variable.
- Debe llamar a la función para ejecutar el código que contiene.
SALIDA
Hello!
Los argumentos de una llamada a función se corresponden con sus parámetros definidos.
- Las funciones son más útiles cuando pueden operar sobre diferentes datos.
- Especifica parámetros al definir una función.
- Se convierten en variables cuando se ejecuta la función.
- Se asignan los argumentos en la llamada (es decir, los valores pasados a la función).
- Si no nombras los argumentos cuando los usas en la llamada, los argumentos se emparejarán con los parámetros en el orden en que los parámetros están definidos en la función.
PYTHON
def print_date(year, month, day):
joined = str(year) + '/' + str(month) + '/' + str(day)
print(joined)
print_date(1871, 3, 19)
SALIDA
1871/3/19
O bien, podemos nombrar los argumentos cuando llamamos a la función, lo que nos permite especificarlos en cualquier orden y añade claridad al sitio de llamada; de lo contrario, mientras uno está leyendo el código podría olvidar si el segundo argumento es el mes o el día, por ejemplo.
SALIDA
1871/3/19
- Vía Twitter:
()
contiene los ingredientes de la función mientras que el cuerpo contiene la receta.
Las funciones pueden devolver un resultado a su invocador usando
return
.
- Utiliza
return ...
para devolver un valor al llamante. - Puede aparecer en cualquier parte de la función.
- Pero las funciones son más fáciles de entender si aparece
return
:- Al principio para tratar casos especiales.
- Al final, con un resultado final.
SALIDA
average of actual values: 2.6666666666666665
SALIDA
average of empty list: None
- Recuerda: toda función devuelve algo.
- Una función que no explícitamente
return
un valor devuelve automáticamenteNone
.
SALIDA
1871/3/19
result of call is: None
Identificación de errores de sintaxis
- Lee el siguiente código e intenta identificar cuáles son los errores sin ejecutarlo.
- Ejecuta el código y lee el mensaje de error. ¿Es un
SyntaxError
o unIndentationError
? - Corrige el error.
- Repite los pasos 2 y 3 hasta que hayas corregido todos los errores.
SALIDA
calling <function report at 0x7fd128ff1bf8> 22.5
Una llamada a una función siempre necesita paréntesis, de lo contrario se obtiene la dirección de memoria del objeto de la función. Por lo tanto, si quisiéramos llamar a la función llamada informe, y darle el valor 22.5 para informar, podríamos tener nuestra llamada a la función de la siguiente manera
SALIDA
calling
pressure is 22.5
Orden de operaciones
- ¿Qué falla en este ejemplo?
PYTHON
result = print_time(11, 37, 59)
def print_time(hour, minute, second):
time_string = str(hour) + ':' + str(minute) + ':' + str(second)
print(time_string)
- Después de solucionar el problema anterior, explica por qué ejecutar este código de ejemplo:
da esta salida:
SALIDA
11:37:59
result of call is: None
- ¿Por qué el resultado de la llamada es
None
?
El problema con el ejemplo es que la función
print_time()
se define después de hacer la llamada a la función. Python no sabe cómo resolver el nombreprint_time
ya que no se ha definido todavía y lanzará unNameError
por ejemplo,NameError: name 'print_time' is not defined
La primera línea de salida
11:37:59
es impresa por la primera línea de código,result = print_time(11, 37, 59)
que vincula el valor devuelto por la invocaciónprint_time
a la variableresult
. La segunda línea es de la segunda llamada de impresión para imprimir el contenido de la variableresult
.print_time()
no devuelve explícitamentereturn
un valor, por lo que devuelve automáticamenteNone
.
Hallar el Primer
Rellena los espacios en blanco para crear una función que tome una lista de números como argumento y devuelva el primer valor negativo de la lista. ¿Qué hace la función si la lista está vacía? ¿Y si la lista no tiene números negativos?
Llamado por su nombre
Anteriormente vimos esta función:
PYTHON
def print_date(year, month, day):
joined = str(year) + '/' + str(month) + '/' + str(day)
print(joined)
Vimos que podemos llamar a la función usando nombres de argumentos, así:
- ¿Qué imprime
print_date(day=1, month=2, year=2003)
? - ¿Cuándo has visto una llamada a una función como ésta?
- ¿Cuándo y por qué es útil llamar así a las funciones?
2003/2/1
- Vimos ejemplos de uso de argumentos con nombre al trabajar
con la librería pandas. Por ejemplo, cuando se lee un conjunto de datos
usando
data = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country')
, el último argumentoindex_col
es un argumento con nombre. - El uso de argumentos con nombre puede hacer que el código sea más legible, ya que uno puede ver en la llamada a la función qué nombre tienen los diferentes argumentos dentro de la función. También puede reducir las posibilidades de pasar argumentos en el orden incorrecto, ya que al utilizar argumentos con nombre el orden no importa.
Encapsulación de un bloque If/Print
El siguiente código se ejecutará en una impresora de etiquetas para huevos de gallina. Una balanza digital informará de la masa de un huevo de gallina (en gramos) al ordenador y éste imprimirá una etiqueta.
PYTHON
import random
for i in range(10):
# simulando la masa de un huevo de gallina
# la masa (aleatoria) será de 70 +/- 20 gramos
mass = 70 + 20.0 * (2.0 * random.random() - 1.0)
print(mass)
# la maquinaria de los huevos de gallina imprime una etiqueta
if mass >= 85:
print("jumbo")
elif mass >= 70:
print("large")
elif mass < 70 and mass >= 55:
print("medium")
else:
print("small")
El bloque if que clasifica los huevos podría ser útil en otras
situaciones, así que para evitar repetirlo, podríamos plegarlo en una
función, get_egg_label()
. Revisando el programa para usar
la función tendríamos esto:
PYTHON
# versíón revisada
import random
for i in range(10):
# simulando la masa de un huevo de gallina
# la masa (aleatoria) será de 70 +/- 20 gramos
mass = 70 + 20.0 * (2.0 * random.random() - 1.0)
print(mass, get_egg_label(mass))
- Cree una definición de función para
get_egg_label()
que funcione con el programa revisado anteriormente. Tenga en cuenta que el valor de retorno de la funciónget_egg_label()
será importante. La salida de ejemplo del programa anterior sería71.23 large
. - Un huevo sucio puede tener una masa superior a 90 gramos, y un huevo
estropeado o roto probablemente tendrá una masa inferior a 50 gramos.
Modifique su función
get_egg_label()
para tener en cuenta estas condiciones de error. Un ejemplo de salida podría ser25 too light, probably spoiled
.
PYTHON
def get_egg_label(mass):
# la maquinaria de los huevos de gallina imprime una etiqueta
egg_label = "Unlabelled"
if mass >= 90:
egg_label = "warning: egg might be dirty"
elif mass >= 85:
egg_label = "jumbo"
elif mass >= 70:
egg_label = "large"
elif mass < 70 and mass >= 55:
egg_label = "medium"
elif mass < 50:
egg_label = "too light, probably spoiled"
else:
egg_label = "small"
return egg_label
Análisis encapsulado de datos
Supongamos que se ha ejecutado el siguiente código:
PYTHON
import pandas as pd
data_asia = pd.read_csv('data/gapminder_gdp_asia.csv', index_col=0)
japan = data_asia.loc['Japan']
- Complete los enunciados siguientes para obtener el PIB medio de Japón en los años indicados para la década de 1980.
PYTHON
year = 1983
gdp_decade = 'gdpPercap_' + str(year // ____)
avg = (japan.loc[gdp_decade + ___] + japan.loc[gdp_decade + ___]) / 2
- Abstrae el código anterior en una única función.
PYTHON
def avg_gdp_in_decade(country, continent, year):
data_countries = pd.read_csv('data/gapminder_gdp_'+___+'.csv',delimiter=',',index_col=0)
____
____
____
return avg
- ¿Cómo generalizaría esta función si no supiera de antemano qué años concretos aparecen como columnas en los datos? Por ejemplo, ¿qué pasaría si también tuviéramos datos de los años que terminan en 1 y 9 para cada década? (Sugerencia: utilice las columnas para filtrar las que corresponden a la década, en lugar de enumerarlas en el código)
- El PIB medio de Japón a lo largo de los años reportados para la década de 1980 se calcula con:
PYTHON
year = 1983
gdp_decade = 'gdpPercap_' + str(year // 10)
avg = (japan.loc[gdp_decade + '2'] + japan.loc[gdp_decade + '7']) / 2
- Que se codifica como una función es:
PYTHON
def avg_gdp_in_decade(country, continent, year):
data_countries = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0)
c = data_countries.loc[country]
gdp_decade = 'gdpPercap_' + str(year // 10)
avg = (c.loc[gdp_decade + '2'] + c.loc[gdp_decade + '7'])/2
return avg
- Para obtener la media de los años relevantes, necesitamos hacer un bucle sobre ellos:
PYTHON
def avg_gdp_in_decade(country, continent, year):
data_countries = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0)
c = data_countries.loc[country]
gdp_decade = 'gdpPercap_' + str(year // 10)
total = 0.0
num_years = 0
for yr_header in c.index: # c's index contains reported years
if yr_header.startswith(gdp_decade):
total = total + c.loc[yr_header]
num_years = num_years + 1
return total/num_years
La función se puede llamar ahora por:
SALIDA
20880.023800000003
Simulación de un sistema dinámico
En matemáticas, un sistema dinámico es un sistema en el que una función describe la dependencia temporal de un punto en un espacio geométrico. Un ejemplo canónico de sistema dinámico es el mapa logístico, un modelo de crecimiento que calcula una nueva densidad de población (entre 0 y 1) a partir de la densidad actual. En el modelo, el tiempo toma valores discretos 0, 1, 2, …
- Definir una función llamada
logistic_map
que toma dos entradas:x
, que representa la población actual (en el momentot
), y un parámetror = 1
. Esta función debe devolver un valor que representa el estado del sistema (población) en el momentot + 1
, utilizando la función de mapeo:
f(t+1) = r * f(t) * [1 - f(t)]
Utilizando un bucle
for
owhile
, itere la funciónlogistic_map
definida en la parte 1, partiendo de una población inicial de 0.5, durante un periodo de tiempot_final = 10
. Almacena los resultados intermedios en una lista, de forma que al finalizar el bucle hayas acumulado una secuencia de valores que representen el estado del mapa logístico en los tiempost = [0,1,...,t_final]
(11 valores en total). Imprima esta lista para ver la evolución de la población.Encapsule la lógica de su bucle en una función llamada
iterate
que toma la población inicial como primera entrada, el parámetrot_final
como segunda entrada y el parámetror
como tercera entrada. La función debe devolver la lista de valores que representan el estado del mapa logístico en los tiempost = [0,1,...,t_final]
. Ejecute esta función para los periodost_final = 100
y1000
e imprima algunos de los valores. ¿La población tiende hacia un estado estacionario?
PYTHON
initial_population = 0.5
t_final = 10
r = 1.0
population = [initial_population]
for t in range(t_final):
population.append( logistic_map(population[t], r) )
PYTHON
def iterate(initial_population, t_final, r):
population = [initial_population]
for t in range(t_final):
population.append( logistic_map(population[t], r) )
return population
for period in (10, 100, 1000):
population = iterate(0.5, period, 1)
print(population[-1])
SALIDA
0.06945089389714401
0.009395779870614648
0.0009913908614406382
La población parece acercarse a cero.
Uso de funciones con condicionales en Pandas
Las funciones a menudo contienen condicionales. He aquí un breve ejemplo que indicará en qué cuartil se encuentra el argumento basándose en los valores codificados a mano para los puntos de corte de los cuartiles.
PYTHON
def calculate_life_quartile(exp):
if exp < 58.41:
# This observation is in the first quartile
return 1
elif exp >= 58.41 and exp < 67.05:
# This observation is in the second quartile
return 2
elif exp >= 67.05 and exp < 71.70:
# This observation is in the third quartile
return 3
elif exp >= 71.70:
# This observation is in the fourth quartile
return 4
else:
# This observation has bad data
return None
calculate_life_quartile(62.5)
SALIDA
2
Esa función se usaría típicamente dentro de un bucle
for
, pero Pandas tiene una forma diferente y más eficiente
de hacer lo mismo, y es aplicando una función a un marco de
datos o a una porción de un marco de datos. He aquí un ejemplo,
utilizando la definición anterior.
PYTHON
data = pd.read_csv('data/gapminder_all.csv')
data['life_qrtl'] = data['lifeExp_1952'].apply(calculate_life_quartile)
Hay muchas cosas en esa segunda línea, así que vayamos por partes. En
el lado derecho de la =
empezamos con
data['lifeExp']
, que es la columna en el marco de datos
llamado data
etiquetado lifExp
. Utilizamos el
apply()
para hacer lo que dice, aplicar el
calculate_life_quartile
al valor de esta columna para cada
fila en el marco de datos.
Puntos Clave
- Descomponer los programas en funciones para facilitar su comprensión.
- Define una función usando
def
con un nombre, parámetros y un bloque de código. - Definir una función no la ejecuta.
- Los argumentos de una llamada a una función coinciden con sus parámetros definidos.
- Las funciones pueden devolver un resultado a su invocador utilizando
return
.