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

Variables y tipos

docs.scrimba.com

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 consultando. Python usa variables para esto: nombres que asignas a valores para poder usarlos en todo tu programa.

Las variables son referencias nombradas a valores. Python vincula un nombre a un objeto en el lado derecho de = y te permite revincularla en cualquier momento. El tipo está en el valor, no en el nombre.

En Python una variable es un vinculación 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, por lo 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 sobre el nombre limita qué puede apuntar.

python
nombre_jugador = "María"
puntuacion = 0
ciudad = "México"

Tres líneas, tres cosas que Python ahora recuerda. Para cada una, Python evalúa el valor en el lado derecho primero, luego lo almacena bajo el nombre en el lado izquierdo. Usa ese nombre después y Python te devuelve el valor.

Cada línea crea una vinculación: el nombre en el lado izquierdo se refiere al objeto en el lado derecho. Python evalúa el lado derecho primero, luego crea la vinculación.

Cada asignación vincula un nombre en el alcance actual (la región del código donde ese nombre es visible, generalmente la función o archivo en el que estás trabajando). Python siempre evalúa el lado derecho completo antes de vincular nada, que 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 se apuntan 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 "igual". Significa almacena este valor bajo este nombre:

python
ciudad = "México"

ciudad obtiene "México". Le estás diciendo a Python: recuerda "México" y etiquétalo ciudad.

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

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

= es asignación: vincula un nombre a un objeto en el alcance actual. Una abreviatura estándar para actualizar una variable es asignación aumentada:

python
puntuacion = 0
puntuacion += 10   # lo mismo que: puntuacion = puntuacion + 10
puntuacion *= 2    # lo mismo que: puntuacion = puntuacion * 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. Por lo tanto, dos nombres pueden terminar apuntando al mismo objeto:

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

b = "world"             # b revinculated a un nuevo objeto
print(id(a) == id(b))   # False
print(a)                # sigue siendo "hello": revincular b no afectó a

id() devuelve la identidad de un objeto, un número único para ese objeto mientras existe, por lo que dos id iguales significan "literalmente el mismo objeto", no dos objetos que resulten ser iguales. La diferencia entre apuntar y copiar es tentadora ignorarla ahora, pero muerde con objetos mutables (aquellos que puedes cambiar en su lugar, 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 tenga. 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
nombre: str = "María"
puntuacion: int = 0
proporcion: float = 0.85
JunoAlmacenar 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 obsesionarme con este.
JunoAlmacenar un valor= apunta un nombre a un valor, y puedes apuntarlo de nuevo en cualquier momento. El nombre es la etiqueta, el valor es la cosa. Ese es el truco completo.
JunoAlmacenar un valor= no deja un valor en una caja, vincula un nombre a un objeto. Reasignar solo mueve el nombre, el objeto anterior se queda. Recuerda eso antes de que aparezcan las listas, o tendrás dos nombres compartiendo silenciosamente una.

Nombrar tus variables

Tú eliges el nombre. Python tiene pocas reglas duras, 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 dolor.

Python hace cumplir 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 y herramienta de Python.

Las reglas que Python realmente hace cumplir 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 hace cumplir, pero linters (herramientas que marcan problemas de estilo y posibles errores), verificadores de tipos y toda base de código profesional lo esperan. Ir en contra principalmente crea fricción para quien lea tu código a continuación.

Reglas que Python hace cumplir:

  • Solo letras, dígitos y guiones bajos. Sin espacios ni guiones.
  • Debe comenzar con una letra o guión bajo, nunca un dígito
  • Sensible a mayúsculas: puntuacion, Puntuacion y PUNTUACION son tres variables separadas

Convenciones que todos siguen (PEP 8):

CosaEstiloEjemplo
Variables y funcionessnake_casenombre_usuario, precio_total
ConstantesUPPER_SNAKE_CASEMAX_REINTENTOS, URL_BASE
ClasesPascalCaseCuentaUsuario, CargadorDatos
python
# nombres claros, legibles de un vistazo
nombre_usuario = "María"
precio_total = 49.99
esta_conectado = True
MAX_REINTENTOS = 3

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

Una trampa que vale la pena conocer temprano: no nombres una variable con un nombre de función integrada de Python como list, input, type o print. Python lo permite, pero silenciosamente romperás la función integrada para el resto de ese alcance y los errores resultantes son difíciles de rastrear.

No hagas sombra a las funciones integradas de Python. Asignar a list, type, input, print o str sobrescribe la función integrada para el resto de ese alcance sin advertencia. Es un error silencioso que puede ser doloroso de encontrar.

UPPER_SNAKE_CASE es una convención, no obligatoria. Python no te detendrá de reasignar MAX_REINTENTOS = 99 después. Es una señal a otros desarrolladores, nada más.

Hacer sombra a una función integrada significa hacer tu propio nombre que coincida con uno que Python ya proporciona (como list). Cuando Python busca un nombre, verifica tus nombres locales antes de los suyos propios, por lo que tu versión gana y la verdadera list se oculta a partir de ese momento. Sigue siendo accesible como builtins.list, pero el código ordinario ya no la ve. En cuanto a constantes, UPPER_SNAKE_CASE es solo una señal visual; Python te dejará felizmente reasignarla. Si quieres una constante que una herramienta pueda realmente hacer cumplir, anótala con typing.Final, que marca el nombre como "no destinado a ser reasignado" para que los verificadores de tipos lo marquen si lo haces.

JunoNombrar tus variables Letras, números, guiones bajos, y comenzar con una letra o guión bajo. Mantente con snake_case y estarás bien. Mi error de principiante: no llames a una variable list o print. Python te lo permite, luego todo se vuelve raro después sin advertencia.
JunoNombrar tus variablessnake_case para nombres, UPPER_SNAKE_CASE para constantes, cada herramienta lo espera. Y no nombres cosas list o print, anularás la función integrada silenciosamente.
JunoNombrar tus variables El intérprete apenas hace cumplir nombres, pero cada linter sí, así que luchar contra snake_case solo te gana dolor. La trampa real es hacer sombra a una función integrada como list: se rompe silenciosamente, y lejos de donde lo hiciste.

Qué puedes almacenar

Python tiene cuatro tipos que usarás en casi todos los programas. Python descubre qué tipo quieres según cómo escribas el valor. Nunca declaras un tipo explícitamente.

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

Cada 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 asociadas a él), por lo que "hi".upper() y (3).bit_length() funcionan directamente en el literal, sin nada que envolver o desenvuelva primero. Raramente tienes que pensarlo, y ese es el punto. Los cuatro tipos a continuación son aquellos a los que recurres en casi todos los programas.

Texto (str)

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

python
nombre_jugador = "María"
ciudad = "México"
mensaje = 'Fin del juego'

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

python
nota = "Es un gran día"
nota = 'Es un gran día'   # mismo resultado

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

python
nombre_jugador = "María"
ciudad = "México"
nota = "Es un gran día"

Un str es una secuencia inmutable de puntos de código Unicode (inmutable significa que nunca se puede cambiar después de crear; los puntos de código son los caracteres en sí, no los bytes brutos en los que se almacenan), por lo que len("café") es 4, no 5. Porque una cadena no puede cambiar, es hashable y puede usarse como clave de diccionario o miembro de conjunto (un valor que nunca cambia puede archivarse de forma segura por su contenido). Una regla que realmente usarás sale 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
nombre_jugador = "María"
ciudad = "México"
nota = "Es un gran día"
JunoTexto (str) El texto va entre comillas, simples o dobles, tu elección. Una vez que existe no puedes cambiarlo en su lugar, y está bien. Cualquier cosa que parezca editar una cadena realmente te devuelve una completamente nueva.
JunoTexto (str) Las cadenas son inmutables. Nada las edita en su lugar, siempre obtienes una nueva cadena. Ten eso en mente el momento en que comiences a encadenar métodos.
JunoTexto (str) Una ejecución inmutable de Unicode, entonces `len("café")` es 4 y las cadenas funcionan como claves dict. El bit que realmente usarás: compara con `==`, nunca `is`.

Números enteros (int)

Los números enteros van sin comillas ni 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
puntuacion = 0
edad = 28
poblacion = 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 mantener 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
puntuacion = 0
edad = 28
poblacion = 8_100_000_000

Dos cosas que vale la pena mantener. Primero, el int de Python es de precisión arbitraria: crece para mantener cualquier número entero que tu RAM pueda ajustar, por lo que no hay overflow (el envolvimiento 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 enteros iguales son el mismo objeto es un detalle de implementación: CPython (el Python estándar) reutiliza enteros pequeños, entonces id(1) == id(1) es True, pero eso silenciosamente deja de sostener para números más grandes, así que nunca lo construyas.

python
puntuacion = 0
edad = 28
poblacion = 8_100_000_000
JunoNúmeros enteros (int) Números enteros, sin comillas, sin punto decimal. Lo mejor, los ints de Python crecen tan grandes como quieras sin extraño overflow. Esos guiones bajos en 8_100_000_000 están solo para que puedas leerlo.
JunoNúmeros enteros (int) Los ints son de precisión arbitraria, así que no hay overflow del que 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 queda sin RAM, así que overflow no es un problema. Un hábito que vale la pena mantener: compara números con ==, no is. La identidad es un capricho de caché, no confíes en ella.

Números decimales (float)

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

python
precio = 4.99
temperatura = 36.6

0.1 + 0.2   # 0.30000000000000004

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

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

python
precio = 4.99
temperatura = 36.6

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

python
precio = 4.99
temperatura = 36.6
JunoNúmeros decimales (float) Un punto decimal lo hace un float, y están bien para cosas cotidianas. El clásico gotcha que todos golpean una vez: 0.1 + 0.2 da 0.30000000000000004. No es un error, eso es 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 es del todo 0.3. Está bien para la mayoría de las matemáticas, nunca para dinero. Usa decimal.Decimal cuando tiene que ser exacto.
JunoNúmeros decimales (float) Los floats derivan, por eso 0.1 + 0.2 se ve roto. La regla: sin floats para dinero o comprobaciones 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 menores en esta etapa, pero cada condición y rama en tu programa se ejecuta en un booleano.

python
esta_conectado = True
hay_errores = 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) todos se comportan 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: ceros, contenedores vacíos y None son falsy; todo lo demás es truthy. Un detalle útil: bool es una subclase de int, entonces True + True evalúa a 2.

python
esta_conectado = True
hay_errores = False

bool se construye sobre int (subclases significa que es una especie especializada de int), y True y False son los únicos dos objetos bool que jamás existen, valen 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 veracidad definiendo __bool__ o __len__ (los métodos especiales "dunder", nombrados con dobles guiones bajos, que Python llama cuando necesita una respuesta de sí o no). Y como un bool es un int, isinstance(True, int) es True, que puede atraparte en código que verifica tipos.

python
esta_conectado = True
hay_errores = 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. Por eso if mi_lista: lee tan bien.
JunoVerdadero o Falso (bool)bool es secretamente un `int`, así que `isinstance(True, int)` es `True`, que te sorprenderá en una verificación de tipo un día. Falsy es ceros, 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 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 verificación de tipos en tu propio código, isinstance() es preferido: maneja la herencia, que las comparaciones type() no lo 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: camina el MRO (el method resolution order, la lista ordenada de clases que Python busca en la cadena de herencia, la línea de tipos principales de los que se construye una clase, expuesta como x.__class__.__mro__), por lo que también devuelve True para tipos principales. Por eso isinstance(True, int) es True (un bool es una clase 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, la verificación que confirma que 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 produce una TypeError:

python
puntuacion = 42
print("Tu puntuación es " + puntuacion)        # TypeError
print("Tu puntuación es " + str(puntuacion))   # funciona

Convierte explícitamente usando el nombre de 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 se puede convertir una cadena decimal a int directamente
int(float("3.14"))3 (convierte a float primero, luego a int)
bool(0) / bool("")False
JunoVerificar y convertir tipostype() te dice qué es algo, isinstance() verifica si es un tipo dado. Y Python no pegará una cadena y un número, así que convierte primero con str() o int(). Todos se tropiezan con eso una vez.
JunoVerificar y convertir tipos Recurre a isinstance() en lugar de 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 exactamente. Las conversiones permanecen explícitas a propósito, `int("3.14")` preferiría lanzar 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: coloca f antes de la comilla de apertura e envuelve cualquier variable en {}. Python lo reemplaza con el valor real de la variable. Los aprenderás correctamente en el próximo capítulo.

python
nombre_jugador = "María"
nivel = 3
precision = 0.94
es_premium = True

print(f"{nombre_jugador} está en el nivel {nivel} con {precision:.0%} de precisión.")
print(f"Cuenta premium: {es_premium}")

Los tipos importan porque nivel + 1 funciona y nombre_jugador + 1 no. Cada variable sostiene exactamente una clase de cosa; Python no las mezclará silenciosamente por ti.

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

python
URL_BASE = "https://api.example.com"
MAX_REINTENTOS = 3
DEBUG = False

nombre_usuario = "María"
conteo_solicitudes = 0
ultima_respuesta = None

conteo_solicitudes += 1
print(f"[{conteo_solicitudes}] {URL_BASE} | debug={DEBUG}")

None es el marcador estándar para "sin valor todavía". Su tipo es NoneType y se comporta como falsy en condiciones. Úsalo como el predeterminado para 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 un nombre tenga. Existe para verificadores de tipo y tu editor (el IDE), y Python la ignora mientras el programa se ejecuta:

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

nombre_usuario: str = "María"
conteo_solicitudes: int = 0
ultima_respuesta: str | None = None

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