Variables, Tipos y Tipado Dinámico
Python está tipado dinámicamente: un nombre puede hacer referencia a diferentes tipos de objetos a lo largo de su vida, mientras que cada objeto aún tiene un tipo concreto en tiempo de ejecución. Comprender la semántica de referencia previene errores sutiles con la mutabilidad y el estado compartido.
Receta
x = [1, 2, 3]
y = x # mismo objeto lista
y.append(4)
print(x) # [1, 2, 3, 4]
a: str = "hola"
print(type(a), isinstance(a, str))Cuándo usar esto:
- Depurando por qué dos variables mutan juntas
- Eligiendo entre copia vs referencia compartida
- Añadiendo sugerencias de tipo sin cambiar el comportamiento en tiempo de ejecución
- Explicando Python a desarrolladores de lenguajes tipados estáticamente
Ejemplo de Trabajo
from copy import deepcopy
def demo_references() -> None:
original = {"items": [1, 2]}
alias = original
shallow = original.copy()
deep = deepcopy(original)
alias["items"].append(3)
shallow["items"].append(4)
deep["items"].append(99)
print("original:", original) # items mutados a través de la lista interna de alias y shallow
print("shallow:", shallow) # el diccionario de nivel superior es nuevo; la lista anidada se comparte
print("deep:", deep) # completamente independiente
def demo_dynamic_typing() -> None:
value: int | str = 42
value = "cuarenta y dos" # legal - el nombre no está bloqueado a int
print(value, type(value))
if __name__ == "__main__":
demo_references()
demo_dynamic_typing()Lo que esto demuestra:
- La asignación enlaza nombres a objetos -
alias = originalcomparte el diccionario dict.copy()es superficial - los objetos mutables anidados permanecen compartidosdeepcopyduplica recursivamente las estructuras anidadas- Las sugerencias de tipo usan
int | strpero no imponen tipos en tiempo de ejecución
Inmersión Profunda
Cómo Funciona
- Nombres vs objetos - Las variables son entradas en un espacio de nombres (diccionario) que apuntan a objetos en el heap.
- Conteo de referencias - CPython rastrea la vida útil de los objetos; cuando el contador de referencias llega a cero, la memoria se recupera.
- Objetos inmutables -
int,str,tuple,frozensetno se pueden cambiar in situ; la "mutación" crea nuevos objetos. - Objetos mutables -
list,dict,setse cambian in situ; los alias ven los mismos cambios. - LEGB - Orden de búsqueda de nombres: Local, Enclosing (delimitador), Global, Built-in (cubierto en profundidad en el artículo de funciones).
Tipos Principales de un Vistazo
| Categoría | Ejemplos | ¿Mutable? |
|---|---|---|
| Numérico | int, float, complex, Decimal | No (reasignar para cambiar) |
| Texto/binario | str, bytes, bytearray | bytearray sí |
| Secuencias | list, tuple, range | list sí |
| Mapeos | dict | Sí |
| Conjuntos | set, frozenset | set sí |
Notas de Python
# id() devuelve la identidad del objeto; is compara la identidad
a = [1]
b = a
c = [1]
print(a is b) # True
print(a is c) # False - objetos lista diferentes
print(a == c) # True - valores igualesTrampas Comunes
- Sorpresas de copia superficial -
list.copy()ycopy.copy()todavía comparten mutables anidados. Solución:deepcopycuando la anidación importa. - Argumentos mutables por defecto -
def f(items=[])reutiliza una lista. Solución:def f(items=None): items = items or []. - Reasignación vs mutación -
x = x + [1]crea una nueva lista;x.append(1)muta in situ. Solución: Saber qué operación pretendes. - Las sugerencias de tipo no son verificaciones en tiempo de ejecución - mypy detecta errores antes del despliegue; Python mismo ignora las sugerencias. Solución: Usar Pydantic o
isinstanceexplícito en los límites. isvs==para enteros/cadenas pequeños - CPython puede internar algunos literales, pero nunca confíes enispara la comparación de valores. Solución: Usar==para valores,issolo para comprobaciones del singletonNone.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
dataclasses con campos tipados | Registros estructurados con sugerencias | Variables de script simples |
| Pydantic models | Validación en tiempo de ejecución en los bordes de la API | Código numérico de bucle interno |
TypedDict | Diccionarios con forma de JSON con forma estática | Objetos que necesitan métodos |
NamedTuple | Registros ligeros inmutables | Necesidad de mutabilidad |
Preguntas Frecuentes
¿Tiene Python variables o referencias?
Ambos términos aparecen en la documentación. Prácticamente: los nombres se enlazan a referencias de objetos. No hay variables al estilo C que contengan valores crudos en la pila para objetos arbitrarios.
¿Por qué cambiar una lista a través de una variable afecta a otra?
Ambos nombres hacen referencia al mismo objeto lista. La asignación nunca clona a menos que copies explícitamente.
¿Se imponen las sugerencias de tipo en tiempo de ejecución?
No. Usa mypy, pyright o Pydantic cuando necesites imposición. Las sugerencias se almacenan en __annotations__ pero son ignoradas por el intérprete.
¿Cuál es la diferencia entre type() e isinstance()?
type(x) is int falla para subclases. Prefiere isinstance(x, int) para comprobaciones polimórficas.
¿Puede una función cambiar una variable del llamador?
Solo si mutas un objeto mutable compartido. Reasignar el nombre de un parámetro no afecta al nombre del llamador.
¿Qué me aporta la inmutabilidad?
Los objetos inmutables son hasheables (cuando sus contenidos son hasheables) y seguros como claves de diccionario. También previenen mutaciones accidentales in situ a través de alias.
¿Cómo copio una estructura anidada?
from copy import deepcopy
clone = deepcopy(nested)¿Por qué usar id()?
Para depurar problemas de identidad - confirmar si dos nombres apuntan al mismo objeto durante la resolución de problemas.
¿x = x + 1 crea un nuevo int?
Sí. Los enteros pequeños pueden ser cacheados, pero semánticamente += en inmutables reasigna el nombre a un nuevo objeto.
¿Cuándo debo añadir sugerencias de tipo a las variables?
Cuando el tipo no es obvio por la asignación, o cuando mypy necesita ayuda para acotar uniones.
Relacionado
- Números, Booleanos y Aritmética - tipos numéricos y veracidad
- Funciones, Argumentos y Ámbito - enlace de parámetros y ámbito
- Inmutabilidad y Hashabilidad - claves hasheables
- Conceptos Básicos de Sugerencias de Tipo - anotando nombres y funciones
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.