Skip to content
This page has been auto-translated and may contain errors.View in English

Variables y tipos

Todo programa necesita recordar cosas. Un cuestionario necesita el nombre del jugador. Un juego necesita la puntuación actual. Un script de clima necesita la ciudad que estás revisando. Python usa variables para esto: nombres que asocias a valores para poder usarlos en todo tu programa.

Las variables son referencias nombradas a valores. Python vincula un nombre a un objeto al lado derecho de = y te deja rebindarlo en cualquier momento. El tipo vive en el valor, no en el nombre.

En Python una variable es un binding de nombre: un nombre que apunta a un objeto (un objeto es cualquier valor que Python mantiene en memoria, como una cadena o un número), nunca una caja etiquetada que contiene el valor en sí. El tipo pertenece al objeto, no al nombre, así que un nombre puede apuntar a un str en una línea y a un int en la siguiente sin error. Esa libertad se llama tipado dinámico: nada acerca del nombre limita lo que le permite apuntar.

python
player_name = "María"
score = 0
city = "Madrid"

Tres líneas, tres cosas que Python ahora recuerda. Para cada una, Python trabaja el valor en el lado derecho primero, luego lo almacena bajo el nombre en el lado izquierdo. Usa ese nombre más tarde y Python te devuelve el valor.

Cada línea crea un binding: el nombre en el lado izquierdo se refiere al objeto en el lado derecho. Python evalúa el lado derecho primero, luego crea el binding.

Cada asignación vincula un nombre en el scope actual (la región de código donde ese nombre es visible, generalmente la función o archivo en el que trabajas). Python siempre evalúa completamente el lado derecho antes de vincular nada, lo cual es exactamente por qué a, b = b, a intercambia dos valores en una línea: el lado derecho se calcula completamente primero, luego ambos nombres apuntan a los resultados.

Almacenando un valor

El signo = confunde a casi todo el que viene de clases de matemáticas. En Python, = no significa "igual". Significa guarda este valor bajo este nombre:

python
city = "Madrid"

city obtiene "Madrid". Le estás diciendo a Python: recuerda "Madrid" y etiquétalo como city.

Puedes reemplazar el valor de una variable en cualquier momento. Python usa el más reciente:

python
score = 0
score = 10   # score es ahora 10
score = 15   # score es ahora 15

= es asignación: vincula un nombre a un objeto en el scope actual. Un atajo estándar para actualizar una variable es asignación aumentada:

python
score = 0
score += 10   # mismo que: score = score + 10
score *= 2    # mismo que: score = score * 2

También puedes vincular múltiples nombres a la vez:

python
x, y, z = 1, 2, 3
a = b = 0        # ambos comienzan en cero

La asignación apunta un nombre a un objeto; nunca copia el valor en un nuevo contenedor. Así que dos nombres pueden terminar apuntando al exactamente mismo objeto:

python
a = "hello"
b = a
print(id(a) == id(b))   # True (ambos nombres apuntan a un objeto)

b = "world"             # b revinculado a un nuevo objeto
print(id(a) == id(b))   # False
print(a)                # aún "hello": revinculando b no afectó a

id() devuelve la identidad de un objeto, un número único para ese objeto mientras existe, así que dos ids iguales significan "literalmente el mismo objeto", no dos objetos que resulten ser iguales. La diferencia entre apuntar y copiar es tentadora de ignorar ahora, pero te causa problemas con objetos mutables (los que puedes cambiar en el sitio, como listas): apunta dos nombres a una lista, cámbiala a través de cualquier nombre, y el cambio aparece en ambos. Las listas y dicts hacen esto concreto en capítulos posteriores.

Una anotación de tipo registra el tipo que esperas que un nombre contenga. Es una nota para herramientas que leen tu código sin ejecutarlo (verificadores de tipos y tu editor), y Python la ignora mientras el programa se ejecuta:

python
name: str = "María"
score: int = 0
ratio: float = 0.85
JunoAlmacenando un valor= no es "igual" de clase de matemáticas, es "guarda este valor bajo este nombre". Cámbialo cuando quieras y Python mantiene el más nuevo. Sin declarar, sin configuración, lo nombras y existe. Me tomó demasiado tiempo dejar de pensar demasiado en esto.
JunoAlmacenando un valor= apunta un nombre a un valor, y puedes reapuntarlo en cualquier momento. El nombre es la etiqueta, el valor es la cosa. Ese es el truco completo.
JunoAlmacenando un valor= no deja caer un valor en una caja, vincula un nombre a un objeto. Reasignar solo mueve el nombre, el objeto viejo se queda. Recuerda eso antes de que aparezcan las listas, o tendrás dos nombres compartiendo silenciosamente una.

Nombrando tus variables

Tú eliges el nombre. Python tiene pocas reglas estrictas, y la comunidad sigue convenciones que vale la pena adoptar desde el primer día. Los nombres claros hacen el código legible semanas después. Los nombres crípticos causan dolor.

Python refuerza un pequeño conjunto de reglas de sintaxis de identificador. Más allá de eso, las convenciones de PEP 8 son el estándar de facto en toda base de código Python y herramienta.

Las reglas que Python refuerza realmente para un identificador (la palabra técnica para un nombre) son mínimas. El resto es PEP 8, la guía de estilo oficial de Python. El intérprete (el programa que ejecuta tu código) no lo refuerza, pero linters (herramientas que señalan problemas de estilo y bugs probables), verificadores de tipos y toda base de código profesional lo esperan. Ir en contra generalmente solo crea fricción para quien lee tu código siguiente.

Reglas que Python refuerza:

  • Solo letras, dígitos y guiones bajos. Sin espacios ni guiones.
  • Debe comenzar con una letra o guion bajo, nunca un dígito
  • Sensible a mayúsculas: score, Score y SCORE son tres variables separadas

Convenciones que todos siguen (PEP 8):

CosaEstiloEjemplo
Variables y funcionessnake_caseuser_name, total_price
ConstantesUPPER_SNAKE_CASEMAX_RETRIES, BASE_URL
ClasesPascalCaseUserAccount, DataLoader
python
# nombres claros, legibles de un vistazo
user_name = "María"
total_price = 49.99
is_logged_in = True
MAX_RETRIES = 3

# te arrepentirás de estos en menos de una hora
x = "María"
tp = 49.99
b = True

Una trampa que vale la pena conocer temprano: no nombres una variable según un built-in de Python como list, input, type o print. Python lo permite, pero romperás silenciosamente el built-in por el resto de ese scope y los errores resultantes son difíciles de rastrear.

No ocultes los built-ins de Python. Asignar a list, type, input, print o str sobrescribe el built-in por el resto de ese scope sin ninguna advertencia. Es un bug silencioso que puede ser doloroso encontrar.

UPPER_SNAKE_CASE es una convención, no reforzada. Python no te impedirá reasignar MAX_RETRIES = 99 después. Es una señal a otros desarrolladores, nada más.

Ocultar un built-in significa hacer tu propio nombre que coincida con uno que Python ya proporciona (como list). Cuando Python busca un nombre, primero verifica tus nombres locales antes de sus propios built-ins, así que tu versión gana y el list real está oculto a partir de ese punto. Aún es accesible como builtins.list, pero el código ordinario ya no lo ve. En cuanto a constantes, UPPER_SNAKE_CASE es solo una señal visual; Python felizmente te dejará reasignarla. Si quieres una constante que una herramienta pueda realmente reforzar, anótala con typing.Final, que marca el nombre como "no destinado a ser reasignado" para que los verificadores de tipos lo señalen si lo haces.

JunoNombrando tus variables Letras, números, guiones bajos, y comienza con una letra o guion bajo. Mantente con snake_case y estás bien. Mi único error de principiante: no llames a una variable list o print. Python te lo permite, luego todo se comporta raro después sin advertencia.
JunoNombrando tus variablessnake_case para nombres, UPPER_SNAKE_CASE para constantes, toda herramienta lo espera. Y no nombres cosas list o print, romperás el built-in silenciosamente.
JunoNombrando tus variables El intérprete apenas refuerza nombres, pero todo linter sí, así que luchar contra snake_case solo te trae problemas. La trampa real es ocultar un built-in como list: rompe silenciosamente, y lejos de donde lo hiciste.

Qué puedes almacenar

Python tiene cuatro tipos que usarás en casi todo programa. Python descubre qué tipo tienes en mente por cómo escribes el valor. Nunca declaras un tipo explícitamente.

Python infiere el tipo de la sintaxis literal. Estos cuatro tipos cubren el espacio de valores fundamental; todo lo demás en el lenguaje se construye sobre ellos.

Todo valor en Python es un objeto completo, incluso un literal (un valor escrito directamente en el código, como 42 o "hi"). Ser un objeto significa que el valor lleva sus propios métodos (funciones adjuntas a él), así que "hi".upper() y (3).bit_length() funcionan directamente en el literal, sin nada que envolver o desenvolver primero. Raramente tienes que pensarlo, y ese es el punto. Los cuatro tipos a continuación son los que usas en casi todo programa.

Texto (str)

Cualquier texto va dentro de comillas, simples o dobles. Las comillas le dicen a Python que significas caracteres literales, no un nombre de variable. Una vez creada, una cadena no puede ser cambiada en el sitio. El capítulo Strings cubre todo lo que puedes hacer con ellas.

python
player_name = "María"
city = "Madrid"
message = 'Game over'

Si tu texto contiene un apóstrofe, usa comillas dobles para evitar tener que escaparlo:

python
note = "It's a great day"
note = 'It\'s a great day'   # mismo resultado, usando un escape

Las cadenas sostienen cualquier texto en comillas simples o dobles. Son inmutables: ninguna operación modifica una cadena en el sitio; toda transformación devuelve una nueva. Esto importa por rendimiento: + repetido dentro de un loop crea un nuevo objeto de cadena en cada paso. El capítulo Strings cubre la alternativa eficiente.

python
player_name = "María"
city = "Madrid"
note = "It's a great day"

Un str es una secuencia inmutable de code points Unicode (inmutable significa que nunca puede ser cambiada después de crearse; code points son los caracteres en sí, no los bytes crudos en los que se almacenan), razón por la cual len("café") es 4, no 5. Porque una cadena no puede cambiar, es hashable y puede ser usada como clave de diccionario o miembro de conjunto (un valor que nunca cambia puede ser guardado seguramente por su contenido). Una regla que realmente usarás surge de esto: compara cadenas con == (mismos caracteres), nunca is (el mismo objeto en memoria), porque si dos cadenas iguales comparten un objeto es un detalle de implementación en el que no puedes confiar.

python
player_name = "María"
city = "Madrid"
note = "It's a great day"
JunoTexto (str) El texto va entre comillas, simples o dobles, tu elección. Una vez que existe no puedes cambiarlo en el sitio, y está bien. Cualquier cosa que parezca editar una cadena realmente te entrega una completamente nueva.
JunoTexto (str) Las cadenas son inmutables. Nada las edita en el sitio, siempre obtienes una nueva cadena. Manten eso en mente cuando comienzes a encadenar métodos.
JunoTexto (str) Una ejecución inmutable de Unicode, así que len("café") es 4 y las cadenas funcionan como claves de dict. El bit que realmente usarás: compara con ==, nunca is.

Números enteros (int)

Los números enteros van sin comillas o punto decimal. Python los llama integers. Pueden ser tan grandes como necesites; Python maneja números arbitrariamente grandes sin ningún esfuerzo especial de tu parte.

python
score = 0
age = 28
population = 8_100_000_000   # los guiones bajos son solo para legibilidad

Los enteros se escriben sin comillas o puntos decimales. Los enteros de Python son precisión arbitraria: crecen para sostener cualquier valor, a diferencia de los enteros de tamaño fijo de 32 o 64 bits en C o Java. Los guiones bajos en literales numéricos son cosméticos e ignorados por Python.

python
score = 0
age = 28
population = 8_100_000_000

Dos cosas vale la pena mantener. Primero, el int de Python es precisión arbitraria: crece para sostener cualquier número entero que tu RAM pueda contener, así que no hay overflow (el wrap-around o error que golpeas en lenguajes con enteros de tamaño fijo) para diseñar. Segundo, compara números con == (valor igual), nunca is (el mismo objeto). Si dos ints iguales son el mismo objeto es un detalle de implementación: CPython (el Python estándar) reutiliza enteros pequeños, así que id(1) == id(1) es True, pero eso silenciosamente deja de sostener para números más grandes, así que nunca confíes en eso.

python
score = 0
age = 28
population = 8_100_000_000
JunoNúmeros enteros (int) Números enteros, sin comillas, sin punto decimal. La mejor parte, los ints de Python crecen tan grandes como quieras sin overflow raro. Esos guiones bajos en 8_100_000_000 están solo para que puedas leerlo.
JunoNúmeros enteros (int) Los ints son precisión arbitraria, así que sin overflow de qué preocuparse. Los guiones bajos en números son cosméticos, úsalos en cualquier cosa con muchos dígitos.
JunoNúmeros enteros (int) Los ints crecen hasta que se te acaba RAM, así que overflow es un no-problema. Un hábito que vale la pena mantener: compara números con ==, no is. La identidad es una peculiaridad de caching, no dependas de eso.

Números decimales (float)

Cualquier número con punto decimal es un float. Funcionan como se espera para la mayoría de cálculos. Una cosa a saber: algunos valores decimales no pueden ser almacenados exactamente en binario, así que puedes obtener un pequeño error de redondeo:

python
price = 4.99
temperature = 36.6

0.1 + 0.2   # 0.30000000000000004

Para trabajo cotidiano esto raramente importa. Para cálculos financieros donde las fracciones de centavo cuentan, Python tiene un módulo decimal que lo maneja correctamente. Eso se cubre en el capítulo Numbers.

Cualquier número con punto decimal se vuelve un float. Los floats de Python son precisión doble: alrededor de 15 a 17 dígitos significativos, almacenados en binario. Ese almacenamiento binario es la trampa bien conocida: 0.1 + 0.2 es 0.30000000000000004, no un bug de Python sino una consecuencia de cómo el binario representa decimales. Para dinero, o en cualquier lugar donde los decimales exactos importan, usa el módulo decimal de Python, cubierto en el capítulo Numbers.

python
price = 4.99
temperature = 36.6

Los floats se almacenan en binario (base 2), y la mayoría de fracciones decimales, cualquier cosa cuyo denominador no sea una potencia de dos (como 1/10), no pueden ser escritas exactamente en binario. De ahí que 0.1 + 0.2 salga como 0.30000000000000004. La regla que importa en producción: nunca uses un float para dinero, o para cualquier cosa que verificarás para igualdad exacta. Usa decimal.Decimal cuando necesites aritmética exacta en base-10, o fractions.Fraction para razones exactas. Ambas vienen con la biblioteca estándar de Python (las herramientas incluidas con Python), cubierto en el capítulo Modules.

python
price = 4.99
temperature = 36.6
JunoNúmeros decimales (float) Un punto decimal lo hace un float, y están bien para cosas cotidianas. La trampa clásica que todos golpean una vez: 0.1 + 0.2 da 0.30000000000000004. No un bug, eso es binario. Para dinero, usa decimal.Decimal.
JunoNúmeros decimales (float) Los floats son binarios, así que algunos decimales no pueden ser exactos, 0.1 + 0.2 no es exactamente 0.3. Bien para la mayoría de math, nunca para dinero. Usa decimal.Decimal cuando tiene que ser exacto.
JunoNúmeros decimales (float) Los floats se desvían, por eso 0.1 + 0.2 se ve roto. La regla: sin floats para dinero o cheques de igualdad exacta. decimal.Decimal para base-10 exacta, fractions.Fraction para razones.

Verdadero o Falso (bool)

Algunas cosas están encendidas o apagadas. Python usa booleanos para esto: exactamente dos valores, True y False. Se ven menores en este punto, pero toda condición y rama en tu programa se ejecuta sobre un booleano.

python
is_logged_in = True
has_errors = False

Python también trata ciertos valores como si fueran False cuando se usan en una condición: 0, 0.0, "" y None (el "sin valor aquí" de Python) se comportan todos como False. Todo lo demás se comporta como True. Esto se vuelve útil en el capítulo Control flow.

bool sostiene exactamente True o False. Es devuelto por comparaciones y consumido por condiciones. Python tiene un conjunto más amplio de valores truthy y falsy: valores cero, contenedores vacíos y None son falsy; todo lo demás es truthy. Un detalle útil: bool es una subclase de int, así que True + True se evalúa a 2.

python
is_logged_in = True
has_errors = False

bool se construye sobre int (lo subclasifica, significando que es una clase especializada de int), y True y False son los únicos dos objetos bool que jamás existen, valiendo exactamente 1 y 0. En una condición, los valores falsy (los que cuentan como falso) son: ceros (0, 0.0), contenedores vacíos ("", [], (), {}), None y False en sí. Todo lo demás es truthy. Tus propias clases pueden decidir su truthiness definiendo __bool__ o __len__ (los métodos especiales "dunder", nombrados con guiones bajos dobles, que Python llama cuando necesita una respuesta sí-o-no). Y porque un bool es un int, isinstance(True, int) es True, que puede atraparte en código que chequea tipos.

python
is_logged_in = True
has_errors = False
JunoVerdadero o Falso (bool) Dos valores, True y False, y están detrás de cada if que jamás escribirás. El bit divertido: 0, "", [] y None todos cuentan como False, todo lo demás cuenta como True.
JunoVerdadero o Falso (bool)bool es True/False de comparaciones. Python también tiene truthy y falsy: cosas vacías, cero y None son falsy. Es por eso que if my_list: se lee tan bien.
JunoVerdadero o Falso (bool)bool es secretamente un int, así que isinstance(True, int) es True, que te sorprenderá en un chequeo de tipo algún día. Falsy es ceros, vacíos y None. Objetos personalizados escogen su propio truthiness con __bool__ o __len__.

Chequeando y convirtiendo tipos

Cuando no estás seguro de qué tipo es un valor, type() te lo dice. Para verificar si un valor es un tipo específico, isinstance() es la herramienta más confiable:

python
print(type("hello"))   # <class 'str'>
print(type(42))        # <class 'int'>
print(type(3.14))      # <class 'float'>
print(type(True))      # <class 'bool'>

isinstance(42, int)    # True
isinstance("hi", str)  # True

type() devuelve el tipo exacto de un objeto. Para chequeando de tipos en tu propio código, isinstance() es preferido: maneja herencia, que comparaciones type() no hacen.

python
print(type(42))          # <class 'int'>
isinstance(True, int)    # True   (bool es una subclase de int)
type(True) == int        # False  (coincidencia exacta solo, sin subclases)

type(x) te da el tipo exacto de x. isinstance(x, T) hace más: camina la MRO (el método resolution order, la lista ordenada de clases que Python busca la cadena de herencia, la línea de tipos padres de los que una clase se construye, expuesta como x.__class__.__mro__), así que también devuelve True para tipos padres. Es por eso que isinstance(True, int) es True (un bool es una clase de int) mientras type(True) == int es False (un chequeo de coincidencia exacta). En código real, usa isinstance() como tu guardián de tipos, el chequeo que confirma un valor es el tipo que esperas antes de usarlo.

python
isinstance(True, int)    # True
type(True) == int        # False

Python no mezcla tipos automáticamente. Concatenar una cadena y un número genera un TypeError:

python
score = 42
print("Your score is " + score)        # TypeError
print("Your score is " + str(score))   # funciona

Convierte explícitamente usando el nombre del tipo como función:

LlamadaResultado
str(42)"42"
int(3.9)3 (trunca, no redondea)
float("3.14")3.14
int("3.14")ValueError: no puede convertir una cadena decimal a int directamente
int(float("3.14"))3 (convierte a float primero, luego a int)
bool(0) / bool("")False
JunoChequeando y convirtiendo tipostype() te dice qué algo es, isinstance() chequea si es un tipo dado. Y Python no pegará una cadena y un número juntos, así que convierte primero con str() o int(). Todo el mundo tropieza con eso una vez.
JunoChequeando y convirtiendo tipos Opta por isinstance() sobre type() ==, respeta herencia. Las conversiones siempre son explícitas, así que convierte antes de concatenar una cadena y un número.
JunoChequeando y convirtiendo tiposisinstance() es tu guardián de tipos, sigue el árbol de clases donde type(x) == T solo coincide exactamente. Las conversiones se mantienen explícitas a propósito, int("3.14") prefiere generar un error que adivinar, así que ve a través de float primero.

En la práctica

Los cuatro tipos trabajando juntos en un pequeño script. Las líneas de salida usan f-strings para incrustar valores en texto: pon f antes de la comilla de apertura y envuelve cualquier variable en {}. Python lo reemplaza con el valor real de la variable. Los aprenderás apropiadamente en el próximo capítulo.

python
player_name = "María"
level = 3
accuracy = 0.94
is_premium = True

print(f"{player_name} is on level {level} with {accuracy:.0%} accuracy.")
print(f"Premium account: {is_premium}")

Los tipos importan porque level + 1 funciona y player_name + 1 no. Cada variable sostiene exactamente un tipo de cosa; Python no los mezclará silenciosamente para ti.

Un bloque de configuración realista con los cuatro tipos, constantes separadas del estado de tiempo de ejecución. La sintaxis f"..." es un f-string: cualquier expresión dentro de {} es evaluada en tiempo de ejecución e incrustada en la salida. Cubierto completamente en el capítulo Output and input.

python
BASE_URL = "https://api.example.com"
MAX_RETRIES = 3
DEBUG = False

user_name = "María"
request_count = 0
last_response = None

request_count += 1
print(f"[{request_count}] {BASE_URL} | debug={DEBUG}")

None es el marcador estándar para "aún sin valor". Su tipo es NoneType y se comporta como falsy en condiciones. Úsalo como el defecto para variables que no son significativas hasta más tarde en el programa.

La misma configuración, ahora con anotaciones de tipo en línea. Una anotación registra el tipo que esperas que un nombre contenga. Existe para verificadores de tipos y tu editor (el IDE), y Python la ignora mientras el programa se ejecuta:

python
BASE_URL: str = "https://api.example.com"
MAX_RETRIES: int = 3
DEBUG: bool = False

user_name: str = "María"
request_count: int = 0
last_response: str | None = None

str | None es un tipo de unión, añadido en Python 3.10: dice que el valor es o una cadena o None. En versiones más antiguas escribes la misma cosa como Optional[str], importado del módulo typing incluido. La forma str | None es la que preferir en Python moderno siempre que tu versión mínima lo permita.