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 quiz necesita el nombre del jugador. Un juego necesita el puntaje actual. Un script del clima necesita la ciudad que estás consultando. Python usa variables para esto: nombres que le asignas a valores para poder usarlos en cualquier parte de tu programa.

Las variables son referencias con nombre a valores. Python asocia un nombre a un objeto en el lado derecho de = y te permite reasignarlo en cualquier momento. El tipo pertenece al valor, no al nombre.

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

python
player_name = "Alicia"
score = 0
city = "Ciudad de México"

Tres líneas, tres cosas que Python ahora recuerda. En cada una, Python primero calcula el valor del lado derecho y luego lo guarda bajo el nombre del lado izquierdo. Cuando usas ese nombre más adelante, Python te devuelve el valor.

Cada línea crea un enlace: el nombre a la izquierda hace referencia al objeto a la derecha. Python evalúa primero el lado derecho y luego crea el enlace.

Cada asignación enlaza un nombre en el ámbito actual (scope: la región de código donde ese nombre es visible, generalmente la función o el archivo en el que estás trabajando). Python siempre calcula todo el lado derecho antes de enlazar nada, y por eso a, b = b, a intercambia dos valores en una sola línea: el lado derecho se computa por completo primero, y luego ambos nombres quedan apuntando a los resultados.

Almacenar un valor ​

El signo = confunde a casi todos los que vienen de la clase de matemáticas. En Python, = no significa "es igual a". Significa guarda este valor bajo este nombre:

python
city = "Ciudad de México"

city recibe "Ciudad de México". Le estás diciendo a Python: recuerda "Ciudad de México" 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 ahora es 10
score = 15   # score ahora es 15

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

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

También puedes enlazar varios nombres a la vez:

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

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

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

b = "mundo"              # b se reasigna a un nuevo objeto
print(id(a) == id(b))   # False
print(a)                # sigue siendo "hola": reasignar b no afectó a a

id() devuelve la identidad de un objeto, un número único para ese objeto mientras exista, así que dos ids iguales significan "literalmente el mismo objeto", no dos objetos que resultan ser iguales. La diferencia entre apuntar y copiar es tentador ignorarla ahora, pero te muerde con los objetos mutables (los que puedes cambiar en el mismo lugar, como las listas): apunta dos nombres a una misma lista, cámbiala a través de cualquiera de los dos nombres, y el cambio aparece en ambos. Las listas y los diccionarios lo dejan bien claro en capítulos posteriores.

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

python
name: str = "Alicia"
score: int = 0
ratio: float = 0.85
JunoAlmacenar un valor= no es el "igual" de la clase de matemáticas, es "guarda este valor bajo este nombre". Cámbialo cuando quieras y Python se queda con el más reciente. No hay que declarar nada, ni preparar nada: lo nombras y ya existe. A mí me tomó demasiado tiempo dejar de darle tantas vueltas a esto.
JunoAlmacenar un valor= hace que un nombre apunte a un valor, y puedes reapuntarlo cuando quieras. El nombre es la etiqueta, el valor es la cosa. Ese es todo el truco.
JunoAlmacenar un valor= no mete un valor en una caja, ata un nombre a un objeto. Reasignar solo mueve el nombre, el objeto anterior se queda donde estaba. Recuerda esto antes de llegar a las listas, o tendrás dos nombres compartiendo silenciosamente uno solo.

Cómo nombrar tus variables ​

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

Python impone un pequeño conjunto de reglas de sintaxis para los identificadores. Más allá de eso, las convenciones de PEP 8 son el estándar de facto en todo código y herramienta de Python.

Las reglas que Python realmente impone para un identificador (el término técnico para un nombre) son mínimas. El resto es PEP 8, la guía de estilo oficial de Python. El interpretador (el programa que ejecuta tu código) no la impone, pero los linters (herramientas que señalan problemas de estilo y errores probables), los verificadores de tipos y todo código base profesional la esperan. Ir en contra de ella, sobre todo, le crea fricción a quien lea tu código después.

Reglas que Python impone:

  • Solo letras, números y guiones bajos. Sin espacios ni guiones medios.
  • Debe empezar con una letra o un guion bajo, nunca con un número
  • Distingue mayúsculas y minúsculas: score, Score y SCORE son tres variables distintas

Convenciones que todos siguen (PEP 8):

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

# te vas a arrepentir de estos en una hora
x = "Alicia"
tp = 49.99
b = True

Una trampa que vale la pena conocer desde temprano: no le pongas a una variable el nombre de algo incorporado en Python, como list, input, type o print. Python te lo permite, pero vas a romper silenciosamente esa función incorporada por el resto de ese ámbito, y los errores resultantes son difíciles de rastrear.

No le hagas shadowing (sombra) a las funciones incorporadas de Python. Asignarle un valor a list, type, input, print o str sobrescribe la función incorporada por el resto de ese ámbito, sin ninguna advertencia. Es un error silencioso que puede ser doloroso de encontrar.

UPPER_SNAKE_CASE es una convención, no algo que se imponga. Python no te va a impedir reasignar MAX_RETRIES = 99 más adelante. Es una señal para otros desarrolladores, nada más.

Hacer shadowing de una función incorporada significa crear tu propio nombre que coincide con uno que Python ya provee (como list). Cuando Python busca un nombre, revisa primero tus nombres locales antes que sus propias funciones incorporadas, así que tu versión gana y el verdadero list queda oculto desde ese punto. Todavía se puede acceder a él como builtins.list, pero el código normal ya no lo ve. En cuanto a las constantes, UPPER_SNAKE_CASE es solo una señal visual; Python te va a dejar reasignarla sin problema. Si quieres una constante que una herramienta pueda hacer cumplir de verdad, anótala con typing.Final, que marca el nombre como "no debería reasignarse", de modo que los verificadores de tipos te lo señalen si lo haces.

JunoCómo nombrar tus variables Letras, números, guiones bajos, y empieza con una letra o un guion bajo. Quédate con snake_case y listo. Mi único error de principiante: no le pongas list o print a una variable. Python te lo permite, y luego todo se pone raro sin ninguna advertencia.
JunoCómo nombrar tus variablessnake_case para los nombres, UPPER_SNAKE_CASE para las constantes, toda herramienta lo espera así. Y no le pongas list o print a nada, o vas a pisotear la función incorporada sin darte cuenta.
JunoCómo nombrar tus variables El interpretador casi no exige nada sobre los nombres, pero todos los linters sí, así que ir contra snake_case solo te trae problemas. La verdadera trampa es hacerle shadowing a algo incorporado como list: se rompe en silencio, y lejos de donde lo causaste.

Qué puedes almacenar ​

Python tiene cuatro tipos que vas a usar en casi todo programa. Python deduce a qué tipo te refieres a partir de cómo escribes el valor. Nunca declaras un tipo explícitamente.

Python infiere el tipo a partir de la sintaxis del literal. Estos cuatro tipos cubren el espacio fundamental de valores; 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 "hola"). Ser un objeto significa que el valor lleva consigo sus propios métodos (funciones adheridas a él), así que "hola".upper() y (3).bit_length() funcionan directamente sobre el literal, sin nada que envolver o desenvolver primero. Casi nunca tienes que pensar en esto, y ese es justamente el punto. Los cuatro tipos de abajo son los que usas en casi todo programa.

Texto (str) ​

Cualquier texto va entre comillas, simples o dobles. Las comillas le indican a Python que te refieres a caracteres literales, no al nombre de una variable. Una vez creada, una cadena no se puede modificar en el mismo lugar. El capítulo Cadenas de texto cubre todo lo que puedes hacer con ellas.

python
player_name = "Alicia"
city = "Ciudad de México"
message = 'Fin del juego'

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

python
note = "Hoy es un gran día, ¿no?"
note = 'Hoy es un gran d\'ia, ¿no?'   # mismo resultado, usando un escape

Las cadenas guardan cualquier texto entre comillas simples o dobles. Son inmutables: ninguna operación modifica una cadena en el mismo lugar; toda transformación devuelve una nueva. Esto importa por rendimiento: usar + repetidamente dentro de un ciclo crea un nuevo objeto de cadena en cada paso. El capítulo Cadenas de texto cubre la alternativa eficiente.

python
player_name = "Alicia"
city = "Ciudad de México"
note = "Hoy es un gran día"

Un str es una secuencia inmutable de puntos de código (code points) Unicode (inmutable significa que nunca puede cambiarse después de creado; los puntos de código son los caracteres en sí, no los bytes crudos con los que se almacenan), por eso len("café") es 4, no 5. Como una cadena no puede cambiar, es hasheable y se puede usar como clave de un diccionario o miembro de un set (un valor que nunca cambia puede archivarse de forma segura según su contenido). De aquí sale una regla que de verdad vas a usar: compara cadenas con == (mismos caracteres), nunca con is (el mismo objeto en memoria), porque si dos cadenas iguales comparten un solo objeto es un detalle de implementación en el que no puedes confiar.

python
player_name = "Alicia"
city = "Ciudad de México"
note = "Hoy es un gran día"
JunoTexto (str) El texto va entre comillas, simples o dobles, como prefieras. Una vez que existe no puedes cambiarla en el mismo lugar, y está bien así. Cualquier cosa que parezca editar una cadena en realidad te devuelve una completamente nueva.
JunoTexto (str) Las cadenas son inmutables. Nada las edita en el mismo lugar, siempre obtienes una cadena nueva de vuelta. Tenlo en mente en cuanto empieces a encadenar métodos.
JunoTexto (str) Una secuencia inmutable de Unicode, así que len("café") es 4 y las cadenas funcionan como claves de diccionario. Lo que de verdad vas a usar: compara con ==, nunca con is.

Números enteros (int) ​

Los números enteros se escriben sin comillas ni punto decimal. Python los llama enteros (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 ni puntos decimales. Los enteros de Python son de precisión arbitraria: crecen para contener 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 y Python los ignora.

python
score = 0
age = 28
population = 8_100_000_000

Vale la pena quedarse con dos cosas. Primero, el int de Python es de precisión arbitraria: crece para contener cualquier número entero que le entre a tu RAM, así que no hay ningún overflow (el desborde o error que ocurre en lenguajes con enteros de tamaño fijo) que tengas que prever. Segundo, compara números con == (valor igual), nunca con is (el mismo objeto). Que dos enteros iguales sean el mismo objeto es un detalle de implementación: CPython (el Python estándar) reutiliza los enteros pequeños, así que id(1) == id(1) es True, pero eso deja de cumplirse silenciosamente con números más grandes, así que nunca dependas de esto.

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

Números decimales (float) ​

Cualquier número con punto decimal es un float. Funcionan como esperas en la mayoría de los cálculos. Algo que hay que saber: algunos valores decimales no se pueden almacenar de forma exacta 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 el trabajo cotidiano esto rara vez importa. Para cálculos financieros donde cuentan las fracciones de centavo, Python tiene un módulo decimal que lo maneja correctamente. Eso se cubre en el capítulo Números.

Cualquier número con punto decimal se convierte en float. Los floats de Python son de doble precisión: entre 15 y 17 cifras significativas, almacenadas en binario. Ese almacenamiento en binario es el problema conocido: 0.1 + 0.2 da 0.30000000000000004, lo cual no es un error de Python sino una consecuencia de cómo el binario representa los decimales. Para dinero, o en cualquier lugar donde importen los decimales exactos, recurre al módulo decimal de Python, cubierto en el capítulo Números.

python
price = 4.99
temperature = 36.6

Los floats se almacenan en binario (base 2), y la mayoría de las fracciones decimales, cualquiera cuyo denominador no sea una potencia de dos (como 1/10), no se pueden escribir de forma exacta en binario. De ahí sale que 0.1 + 0.2 termine dando 0.30000000000000004. La regla que importa en producción: nunca uses un float para dinero, ni para nada que vayas a comparar por igualdad exacta. Recurre a decimal.Decimal cuando necesites aritmética exacta en base 10, o a fractions.Fraction para razones exactas. Ambos vienen con la biblioteca estándar de Python (las herramientas incluidas con el propio Python), cubierto en el capítulo Módulos.

python
price = 4.99
temperature = 36.6
JunoNúmeros decimales (float) Un punto decimal lo convierte en float, y son perfectos para el uso diario. El clásico tropiezo con el que todos se topan una vez: 0.1 + 0.2 da 0.30000000000000004. No es un error, así es el binario. Para dinero, recurre a decimal.Decimal.
JunoNúmeros decimales (float) Los floats son binarios, así que algunos decimales no pueden ser exactos, 0.1 + 0.2 no da exactamente 0.3. Bien para la mayoría de las matemáticas, nunca para dinero. Usa decimal.Decimal cuando tenga que ser exacto.
JunoNúmeros decimales (float) Los floats se desvían, por eso 0.1 + 0.2 se ve roto. La regla: nada de floats para dinero ni para comparaciones 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. Parecen poco importantes en esta etapa, pero cada condición y cada rama de tu programa se basa en 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 "aquí no hay valor" de Python) se comportan todos como False. Todo lo demás se comporta como True. Esto se vuelve útil en el capítulo Flujo de control.

bool guarda exactamente True o False. Lo devuelven las comparaciones y lo consumen las condiciones. Python tiene un conjunto más amplio de valores truthy (verdaderos) y falsy (falsos): los valores cero, los 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 como 2.

python
is_logged_in = True
has_errors = False

bool está construido sobre int (lo subclasifica, es decir, es un tipo especializado de int), y True y False son los únicos dos objetos bool que existen, con valores exactamente 1 y 0. En una condición, los valores falsy (los que cuentan como falso) son: los ceros (0, 0.0), los contenedores vacíos ("", [], (), {}), None y el propio False. Todo lo demás es truthy. Tus propias clases pueden decidir su propia veracidad definiendo __bool__ o __len__ (los métodos especiales "dunder", nombrados con doble guion bajo, que Python llama cuando necesita una respuesta de sí o no). Y como un bool es un int, isinstance(True, int) es True, lo cual puede sorprenderte en código que verifica 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 vayas a escribir. La parte divertida: 0, "", [] y None cuentan todos como False, todo lo demás cuenta como True.
JunoVerdadero o Falso (bool)bool es True/False que viene de las comparaciones. Python también tiene truthy y falsy: lo vacío, el cero y None son falsy. Por eso if my_list: se lee tan bien.
JunoVerdadero o Falso (bool)bool es secretamente un int, así que isinstance(True, int) es True, y eso te va a sorprender algún día en una verificación de tipo. Falsy son los ceros, los vacíos y None. Los objetos personalizados eligen su propia veracidad con __bool__ o __len__.

Verificar y convertir tipos ​

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

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

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

type() devuelve el tipo exacto de un objeto. Para verificar tipos en tu propio código, se prefiere isinstance(): maneja la herencia, algo que las comparaciones con type() no hacen.

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

type(x) te da el tipo exacto de x. isinstance(x, T) hace más: recorre el MRO (el method resolution order u orden de resolución de métodos, la lista ordenada de clases que Python recorre siguiendo la cadena de herencia, la línea de tipos padre de los que se construye una clase, expuesta como x.__class__.__mro__), así que también devuelve True para los tipos padre. Por eso isinstance(True, int) es True (un bool es un tipo de int) mientras que type(True) == int es False (una verificación de coincidencia exacta). En código real, recurre a isinstance() como tu guardia de tipo (type guard), la verificación que confirma que un valor es del 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 lanza un TypeError:

python
score = 42
print("Tu puntaje es " + score)        # TypeError
print("Tu puntaje es " + str(score))   # funciona

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

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

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 el texto: pon f antes de la comilla de apertura y envuelve cualquier variable en {}. Python la reemplaza con el valor real de la variable. Las aprenderás bien en el próximo capítulo.

python
player_name = "Alicia"
level = 3
accuracy = 0.94
is_premium = True

print(f"{player_name} está en el nivel {level} con {accuracy:.0%} de precisión.")
print(f"Cuenta premium: {is_premium}")

Los tipos importan porque level + 1 funciona y player_name + 1 no. Cada variable guarda exactamente un tipo de cosa; Python no los va a mezclar silenciosamente por ti.

Un bloque de configuración realista con los cuatro tipos, con las constantes separadas del estado en tiempo de ejecución. La sintaxis f"..." es un f-string: cualquier expresión dentro de {} se evalúa en tiempo de ejecución y se incrusta en la salida. Se cubre completo en el capítulo Salida y entrada.

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

user_name = "Alicia"
request_count = 0
last_response = None

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

None es el marcador de posición estándar para "todavía no hay valor". Su tipo es NoneType y se comporta como falsy en las condiciones. Úsalo como valor por defecto para las variables que no tienen sentido hasta más adelante en el programa.

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

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

user_name: str = "Alicia"
request_count: int = 0
last_response: str | None = None

str | None es un tipo unión, agregado en Python 3.10: indica que el valor es una cadena o None. En versiones anteriores escribes lo mismo como Optional[str], importado del módulo incorporado typing. La forma str | None es la que conviene preferir en Python moderno siempre que tu versión mínima lo permita.