Declaración de funciones en Python
Tipado y funciones: estático vs. dinámico
Un recordatorio rápido de cómo se diferencian Kotlin y Python en cuanto al sistema de tipos:
- Kotlin: tipado estático con inferencia y nulabilidad explícita. Los errores de tipo se detectan en compilación.
def function_name(param_1: Type1, param_2: Type2) -> ReturnType:
# cuerpo de la función
return result Explicación de la sintaxis
-
: palabra clave para declarar una función.defdef -
: nombre en snake_case.function_namefunction_name -
: parámetros opcionales con anotaciones de tipo.(param: Type)(param: Type) -
: tipo de retorno, opcional.-> ReturnType-> ReturnType - Cuerpo: bloque indentado; usa
para devolver valores.returnreturn
Comparación rápida con Kotlin
En Python, las anotaciones de tipo son sugerencias que pueden validarse con herramientas externas, pero el intérprete las ignora en tiempo de ejecución.
Kotlin, en cambio, sí valida los tipos y la nulabilidad en compilación, ofreciendo seguridad antes de ejecutar el programa.
Estilo de nombres
En Python, las funciones y variables deben nombrarse usando la convención snake_case, de acuerdo a las guías de estilo de PEP 8.
- El nombre comienza con una letra minúscula.
- Cada palabra siguiente se separa con un guion bajo.
Ejemplos correctos:
-
calculate_totalcalculate_total -
print_messageprint_message -
mainmain
Usar un estilo de nombres consistente mejora la legibilidad y asegura que tu código esté alineado con las prácticas idiomáticas de Python.
Estilos incorrectos
Evita estilos heredados de otros lenguajes o que no son válidos en Python:
-
— PascalCase, reservado para clases.CalculateTotalCalculateTotal -
— camelCase, típico en Java/Kotlin, no se utiliza en funciones de Python.calculateTotalcalculateTotal -
— kebab-case, inválido como identificador.calculate-totalcalculate-total -
— UPPER_SNAKE_CASE, reservado para constantes.CALCULATE_TOTALCALCULATE_TOTAL
Ejemplo: Sumar dos números
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
# Example usage of the add function
print("Adding 2 and 3 gives:", add(2, 3)) # ➜ 5
# At runtime, Python allows this and produces '12' (concatenation).
# However, ty/mypy/pyright will flag a *type error* (expected ints).
print("Adding '1' and '2' gives:", add("1", "2")) # ➜ 12Argumentos por defecto y nombrados
Puedes definir valores por defecto y usar argumentos nombrados para mejorar la legibilidad:
def summon(character: str, location: str = "Rivendell") -> str:
return f"{character} has been summoned to {location}."
if __name__ == "__main__":
print(summon("Gandalf")) # ➜ Gandalf has been summoned to Rivendell.
print(
summon(character="Aragorn", location="Minas Tirith")
) # ➜ Aragorn has been summoned to Minas Tirith. Valores por defecto mutables: bug clásico
En Python, los valores por defecto de los parámetros se evalúan una sola vez al definir la función. Si usas una lista, dict o set como valor por defecto, se compartirá entre invocaciones y acumulará estado.
from typing import Any
def log_event(
event: dict[str, Any], buffer: list[dict[str, Any]] = []
) -> list[dict[str, Any]]:
buffer.append(event)
return buffer
if __name__ == "__main__":
event_a = {
"trainer": "Brendan",
"action": "catch",
"pokemon": "Ralts",
"location": "Route 102",
}
event_b = {
"trainer": "May",
"action": "badge",
"badge": "Stone Badge",
"gym": "Rustboro City",
}
a = log_event(event_a)
b = log_event(event_b)
print(a is b) # → True; same shared object
print(len(b)) # → 2; Brendan and May's events mixed
print([e["trainer"] for e in b]) # → ['Brendan', 'May'] Cómo solucionarlo
None None
(referencia)
from typing import Any
def buffer_event_safe(
event: dict[str, Any],
buffer: list[dict[str, Any]] | None = None,
) -> list[dict[str, Any]]:
if buffer is None:
buffer = []
buffer.append(event)
return buffer
if __name__ == "__main__":
event_a = {
"trainer": "Brendan",
"action": "catch",
"pokemon": "Ralts",
"location": "Route 102",
}
event_b = {
"trainer": "May",
"action": "badge",
"badge": "Stone Badge",
"gym": "Rustboro City",
}
safe_a = buffer_event_safe(event_a)
safe_b = buffer_event_safe(event_b)
print(safe_a is safe_b) # → False; different objects
print(len(safe_b)) # → 1; only May's event
print([e["trainer"] for e in safe_b]) # → ['May'] ¿Qué acabamos de hacer?
- Los objetos mutables como valores por defecto se evalúan una sola vez y se comparten entre invocaciones.
- El uso de
como centinela permite crear una nueva lista en cada llamada.NoneNone - La anotación
mantiene la intención clara y verificable por herramientas de análisis estático como ty, mypy o Pyright.list[dict[str, Any]] | Nonelist[dict[str, Any]] | None - Si se pasa un buffer explícito, la mutación es intencional; si no, se crea una lista fresca de manera segura.
Funciones variádicas ( *args *args y **kwargs **kwargs )
*args *args **kwargs **kwargs
En Python, los argumentos posicionales capturados por
se agrupan en una tupla, y los argumentos con nombre
capturados por
*args *args se agrupan en un diccionario. Es decir, la llamada se transforma internamente en estos contenedores
estándar.
**kwargs **kwargs
Veamos un ejemplo centrado en tipos: distingue el tipo del
contenedor ( / tuple tuple )
y el tipo de cada elemento almacenado en ellos.
dict dict
Ejercicio: ¿Qué imprime el siguiente código?
def variadic_types(*args: int, **kwargs: int | str) -> str:
return "\n".join(
(
f"args: {type(args)}", # type of "*args"
*map(lambda a: f"{a}: {type(a)}", args), # type of each element in "*args"
f"kwargs: {type(kwargs)}", # type of "**kwargs"
*map(
# type of each key-value pair in "**kwargs"
lambda kv: f"({kv[0]}: {type(kv[0])}) -> ({kv[1]}: {type(kv[1])})",
kwargs.items(),
),
)
)
if __name__ == "__main__":
print(
variadic_types(1, 2, k1="v1", k2=3)
) Explicación mínima
concatena líneas en un único "\n".join(...)" "\n".join(...)" .
str str aplica f a cada elemento
para producir las líneas sin usar bucles. map(f, iterable) map(f, iterable) recorre las parejas clave–valor como tuplas kwargs.items() kwargs.items() .
(key, value) (key, value)
El objetivo es evidenciar cómo *args y **kwargs empaquetan los argumentos (tupla/diccionario) y cómo inspeccionar el tipo de cada elemento.
# [!code focus:0]
args: <class 'tuple'>
1: <class 'int'>
2: <class 'int'>
Kwargs: <class 'dict'>
(k1: <class 'str'>) -> (v1: <class 'str'>)
(k2: <class 'str'>) -> (3: <class 'int'>)TODO: completar esta sección
from functools import reduce
from operator import add
def sum_positives(*nums: int) -> int:
print(type(nums)) # Outputs: <class 'tuple'>
return reduce(add, filter(lambda n: n > 0, nums), 0)
if __name__ == "__main__":
print(sum_positives(1, -2, 3, 4, -5)) # Outputs: 8
print(sum_positives()) # Outputs: 0
print(sum_positives(-1, -2, -3)) # Outputs: 0