Conceptos de Python 🐍
🐍 Conceptos clave de Python que todo desarrollador debe dominar
1. Objetos mutables e inmutables
En Python, todo es un objeto. Sin embargo, algunos objetos pueden modificarse (mutables) y otros no (inmutables).
Comprender esta diferencia es esencial para evitar errores al pasar datos entre funciones o modificar estructuras de datos.
def añadir_item(items, valor):
items.append(valor)
return items
mi_lista = [1, 2, 3]
añadir_item(mi_lista, 4)
print(mi_lista) # [1, 2, 3, 4] - modificada en su lugarLa clave:
- Tipos inmutables (
int,float,str,tuple) → los cambios crean un nuevo objeto. - Tipos mutables (
list,dict,set) → los cambios afectan al mismo objeto.
1.1. Ejercicios
Crea una función que reciba una lista y le añada un número. Comprueba si la lista original cambia fuera de la función.
Explica qué sucede en este código y por qué los resultados son distintos:
a = (1, 2) b = [1, 2] a += (3,) b += [3] print(a, b)
📘 Documentación oficial:
- Built-in Types — Python 3
📖 Lectura recomendada: - Mutable vs Immutable Objects in Python – Real Python
2. Argumentos mutables por defecto
Los valores por defecto en las funciones de Python solo se evalúan una vez, cuando se define la función, no cada vez que se llama.
Por eso, usar listas o diccionarios como valores por defecto puede generar comportamientos inesperados.
def añadir_a_lista(valor, items=[]):
items.append(valor)
return items
print(añadir_a_lista(1)) # [1]
print(añadir_a_lista(2)) # [1, 2] - no es una lista nueva✅ Solución correcta:
def añadir_a_lista(valor, items=None):
if items is None:
items = []
items.append(valor)
return items2.1. Ejercicios
- Implementa una función
agregar_usuario(nombre, lista=[])y muestra por qué este diseño es peligroso. - Corrige el problema usando
Nonecomo valor por defecto y explica por qué la corrección evita el fallo.
📘 Documentación oficial:
- Default Argument Values — Python Tutorial
📖 Lectura recomendada: - Python Mutable Defaults Are The Source of All Evil – Florimond Manca
3. Paso de parámetros por referencia de objeto
Python no usa estrictamente “paso por valor” ni “paso por referencia”.
En su lugar, pasa referencias a los objetos, lo que significa que los cambios en objetos mutables afectan a la variable original.
def modificar(num):
num += 1
print("Dentro:", num)
x = 5
modificar(x)
print("Fuera:", x) # sigue siendo 5🔍 Para los tipos inmutables, parece “por valor”.
Para los mutables, se comporta “por referencia”.
3.1. Ejercicios
- Crea una función que modifique una lista dentro de otra función y comprueba si el cambio se refleja fuera.
- Explica la diferencia al pasar una lista y una tupla a una función que intenta modificarlas.
📘 Documentación oficial:
- Data model — Python 3
📖 Lectura recomendada: - Pass by Object Reference in Python – Medium
4. is vs ==
En Python, == compara valores y is compara identidad de objeto.
Esta distinción es crítica, especialmente al trabajar con None o pequeños objetos internos que Python reutiliza.
a = [1, 2]
b = [1, 2]
print(a == b) # True
print(a is b) # FalseConsejos
Consejo: usa siempre is None y is not None al comprobar valores nulos.
4.1. Ejercicios
- Explica por qué
x is ypuede serTruepara enteros pequeños pero no para listas iguales. - Crea una función que reciba un valor y determine si es
Noneo no usandois.
📘 Documentación oficial:
- Identity, Equality, and Type — Data Model
📖 Lectura recomendada: - Is vs == in Python – Real Python
5. Iteradores y generadores
Los iteradores son objetos que se recorren elemento a elemento con next().
Los generadores son una forma sencilla de crear iteradores sin almacenar todos los datos en memoria.
def cuenta_atras(n):
while n > 0:
yield n
n -= 1
for i in cuenta_atras(3):
print(i)Nota
Los generadores permiten escribir código eficiente y legible.
5.1. Ejercicios
- Crea un generador que devuelva los cuadrados de los números del 1 al 5.
- Usa
iter()ynext()para recorrer una lista manualmente, manejandoStopIteration.
📘 Documentación oficial:
- Iterators — Python Docs
📖 Lectura recomendada: - Iterators and Iterables in Python – Real Python
6. List comprehensions vs generator expressions
Ambas permiten crear secuencias de forma concisa, pero las comprensiones crean listas completas, mientras que los generadores producen elementos uno a uno (más eficientes en memoria).
cuadrados_lista = [x*x for x in range(5)]
cuadrados_gen = (x*x for x in range(5))6.1. Ejercicios
- Crea una lista de números pares entre 1 y 20 usando comprensión de listas.
- Convierte el ejercicio anterior en una expresión generadora y usa
next()para obtener valores.
📘 Documentación oficial:
- Expressions — Python Reference
📖 Lectura recomendada: - List Comprehensions vs Generator Expressions – GeeksforGeeks
7. Administradores de contexto (with)
Los context managers permiten manejar recursos (archivos, conexiones, etc.) asegurando su liberación, incluso si ocurre un error.
Se implementan fácilmente con la palabra clave with.
with open("datos.txt") as f:
datos = f.read()🔒 Garantizan el cierre o limpieza del recurso de forma automática.
7.1. Ejercicios
- Crea un programa que lea un archivo usando
withy cuente sus líneas. - Implementa una clase
MiContextocon__enter__y__exit__que imprima mensajes al entrar y salir.
📘 Documentación oficial:
- Context Managers — Python Tutorial
📖 Lectura recomendada: - Context Managers and the with Statement – Real Python
8. El poder de *args y **kwargs
*args y **kwargs permiten definir funciones que aceptan un número variable de argumentos.
Son muy útiles para crear funciones flexibles y reutilizables.
def demo(a, *args, **kwargs):
print("a:", a)
print("args:", args)
print("kwargs:", kwargs)
demo(1, 2, 3, x=4, y=5)8.1. Ejercicios
- Crea una función
mostrar_datosque reciba cualquier cantidad de argumentos y los imprima. - Usa
**kwargspara mostrar información de usuario (nombre,edad,email).
📘 Documentación oficial:
- Defining Functions — Python Tutorial
📖 Lectura recomendada: - Understanding *args and **kwargs in Python – Real Python
9. Decoradores
Los decoradores son funciones que modifican el comportamiento de otras funciones sin alterar su código.
Son útiles para registrar, validar, medir tiempos o aplicar patrones de diseño.
def log(func):
def envoltorio(*args, **kwargs):
print(f"Llamando a {func.__name__}")
return func(*args, **kwargs)
return envoltorio
@log
def saludar(nombre):
print(f"Hola, {nombre}")
saludar("Python")9.1. Ejercicios
- Crea un decorador que mida el tiempo de ejecución de una función.
- Aplica un decorador que registre las llamadas y los argumentos de una función.
📘 Documentación oficial:
- Decorators — Python Tutorial
📖 Lectura recomendada: - Primer on Python Decorators – Real Python
10. __name__ == "__main__"
Esta condición permite que un módulo de Python se ejecute como script principal o como módulo importado.
Evita que se ejecute código no deseado al importar un archivo.
def main():
print("Ejecutando como script!")
if __name__ == "__main__":
main()10.1. Ejercicios
- Crea un archivo con esta estructura y ejecútalo directamente y desde otro script para ver la diferencia.
- Explica por qué
__name__toma distintos valores según cómo se ejecute el archivo.
📘 Documentación oficial:
- Modules — Python Tutorial
📖 Lectura recomendada: - What Does if name == "main" Do? – Real Python