Variables y tipos de datos en Python: la guía que ojalá hubiera tenido cuando empecé
- ¿Qué es una variable? La metáfora de la caja (y por qué está mal)
- Los 5 tipos de datos fundamentales
- int: números enteros sin sorpresas
- float: decimales y sus trampas
- str: cadenas de texto y sus superpoderes
- bool: verdadero, falso y los valores «truthy»
- None: el valor que significa «nada»
- Tipado dinámico: lo que hace diferente a Python
- Conversión de tipos: int(), float(), str(), bool()
- Nombrar variables como un profesional
- Siguiente paso: operadores
- Preguntas frecuentes
Cuando empecé con Python, pensaba que las variables eran «cajitas donde guardas cosas». Es una metáfora cómoda, pero resulta que está mal, y esa imprecisión inicial me causó varios errores que no entendía. En esta lección vamos a ver las variables y los tipos de datos de la manera correcta, y de paso aprenderemos algo sobre cómo Python gestiona la memoria que muchos tutoriales para principiantes nunca explican.
Si vienes de Java, C# o cualquier lenguaje de tipado estático, prepárate para que algunas cosas te sorprendan. Si es tu primer lenguaje, mejor todavía: aprenderás desde el principio cómo funciona Python de verdad, sin malos hábitos importados de otros sitios.
🏷️ ¿Qué es una variable? La metáfora de la caja (y por qué está mal)
La explicación clásica dice: «una variable es como una caja donde guardas un valor». Intuitivo, fácil de visualizar. Pero Python no funciona así exactamente, y entender la diferencia te va a salvar de más de un quebradero de cabeza.
En Python, una variable es más bien una etiqueta que apunta a un objeto en memoria. El objeto tiene el valor y el tipo. La variable es simplemente el nombre que usas para referirte a ese objeto. Ejemplo:
# Python crea el objeto 42 en memoria y "x" apunta a él x = 42 # Ahora "y" apunta al MISMO objeto 42, no a una copia y = x # Verificar que apuntan al mismo objeto print(x is y) # True — mismo objeto en memoria print(id(x)) # 140234567890 (dirección en memoria) print(id(y)) # 140234567890 — ¡la misma! # Cuando reasignas x, NO modificas el 42 original. # Simplemente x ahora apunta a un nuevo objeto 100. x = 100 print(y) # 42 — y sigue apuntando al 42 original
¿Por qué importa esto? Porque cuando haces y = x no estás creando una copia del valor — estás haciendo que dos etiquetas apunten al mismo objeto. Para los tipos básicos (int, float, str, bool) esto no causa problemas porque son inmutables: no puedes modificar el objeto, solo puedes apuntar a otro. Pero cuando lleguemos a listas y diccionarios, este comportamiento te va a importar mucho.
id(variable) devuelve el identificador único del objeto en memoria (básicamente su dirección). Es una herramienta excelente para entender qué está pasando cuando trabajas con variables. No la usarás en producción, pero para aprender es oro.
🧩 Los 5 tipos de datos fundamentales
Python tiene muchos tipos de datos, pero cuando empiezas, solo necesitas conocer cinco. Son los que usarás el 95% del tiempo en las primeras semanas:
Vamos a ver cada uno en detalle, con sus peculiaridades y las trampas que pilla a los principiantes.
🔢 int: números enteros sin sorpresas
El tipo int representa números enteros: positivos, negativos y el cero. La gran diferencia con otros lenguajes es que en Python los enteros no tienen límite de tamaño. En Java un int se desborda si supera ~2.100 millones. En Python puedes calcular factoriales astronómicos sin problema:
# Enteros normales edad = 28 temperatura = -5 contador = 0 # Literales con separador de miles (Python 3.6+) poblacion_esp = 47_431_256 # más legible que 47431256 distancia_km = 1_000_000 # Python maneja enteros arbitrariamente grandes factorial_100 = 1 for i in range(1, 101): factorial_100 *= i print(len(str(factorial_100)), "dígitos") # 158 dígitos — sin desbordamiento # Bases numéricas hex_val = 0xFF # hexadecimal → 255 bin_val = 0b1010 # binario → 10 oct_val = 0o17 # octal → 15 print(hex_val, bin_val, oct_val) # 255 10 15
El separador de miles con guión bajo (47_431_256) es uno de esos pequeños detalles de Python que me encantan. No afecta al valor, pero hace el código mucho más legible cuando trabajas con números grandes. Algo que Java y JavaScript no tienen nativamente.
type(valor) devuelve el tipo de cualquier objeto. Es tremendamente útil mientras aprendes: type(42) devuelve <class 'int'>, type("hola") devuelve <class 'str'>. Úsala siempre que no estés seguro de qué tipo tiene una variable.
🔣 float: decimales y sus trampas
El tipo float representa números con punto decimal. Internamente usa el estándar IEEE 754 de doble precisión, que es el mismo que usan Java, C, JavaScript y prácticamente todos los lenguajes modernos. Y eso implica una trampa que pilla a todo el mundo:
# Floats normales precio = 9.99 pi = 3.14159 altura = 1.72 # La trampa clásica del punto flotante print(0.1 + 0.2) # 0.30000000000000004 ← ¡no es 0.3! print(0.1 + 0.2 == 0.3) # False ← esto te va a sorprender # La forma correcta de comparar floats import math print(math.isclose(0.1 + 0.2, 0.3)) # True ← así se hace # Para dinero: usar el módulo decimal from decimal import Decimal precio_exacto = Decimal("9.99") iva_exacto = Decimal("0.21") print(precio_exacto * iva_exacto) # 2.0979 — exacto # Notación científica muy_grande = 1.5e10 # 15000000000.0 muy_pequeno = 2.5e-4 # 0.00025
El tema de 0.1 + 0.2 == 0.3 siendo False es el bug más famoso de la informática que no es un bug. Es así en todos los lenguajes que usan IEEE 754, y eso incluye prácticamente todos. La razón: 0.1 en binario es un número periódico (como 1/3 en decimal), y la representación se trunca, generando un pequeño error.
decimal de la librería estándar. Los errores de punto flotante en aplicaciones financieras pueden acumularse y generar discrepancias reales. Decimal("9.99") en lugar de 9.99.
📝 str: cadenas de texto y sus superpoderes
Las cadenas de texto (strings) en Python son una de sus partes más potentes. Un str es una secuencia inmutable de caracteres Unicode, lo que significa que puedes trabajar con cualquier idioma, emoji o símbolo sin configuración extra.
# Tres formas de crear strings (todas equivalentes) s1 = 'comillas simples' s2 = "comillas dobles" s3 = """comillas triples para texto multilínea""" # Strings con caracteres especiales ruta = 'C:\\Users\\Ana\\documentos' # \\ para barra invertida ruta2 = r'C:\Users\Ana\documentos' # raw string, más limpio nueva_linea = "primera línea\nsegunda línea" # Acceso por índice — los índices empiezan en 0 nombre = "Python" print(nombre[0]) # P — primer carácter print(nombre[-1]) # n — último carácter print(nombre[1:4]) # yth — slice (del índice 1 al 3) print(nombre[:3]) # Pyt — desde el principio hasta el 2 print(nombre[::-1]) # nohtyP — invertir la cadena # Métodos más útiles frase = " Hola, Mundo! " print(frase.strip()) # "Hola, Mundo!" — quita espacios print(frase.lower()) # " hola, mundo! " print(frase.upper()) # " HOLA, MUNDO! " print(frase.strip().split(",")) # ["Hola", " Mundo!"] print("Python" in frase) # False print(frase.replace("Mundo", "Python")) # " Hola, Python! "
Una cosa que me sorprendió cuando empecé: en Python puedes acceder a los caracteres de una cadena con índices negativos. nombre[-1] es el último carácter, nombre[-2] el penúltimo, y así. Parece un detalle menor, pero cuando quieres el último elemento de algo es mucho más elegante que nombre[len(nombre)-1].
| Método | Qué hace | Ejemplo | Resultado |
|---|---|---|---|
.strip() | Elimina espacios en los extremos | " hola ".strip() | "hola" |
.lower() / .upper() | Convierte a minúsculas / mayúsculas | "PYTHON".lower() | "python" |
.split(sep) | Divide en lista usando separador | "a,b,c".split(",") | ["a","b","c"] |
.replace(a, b) | Reemplaza todas las ocurrencias | "hola".replace("l","r") | "hora" |
.startswith(s) | ¿Empieza por s? | "Python".startswith("Py") | True |
.find(s) | Posición de la primera aparición | "Python".find("th") | 2 |
.count(s) | Cuántas veces aparece s | "banana".count("a") | 3 |
.join(lista) | Une lista en cadena con separador | "-".join(["a","b","c"]) | "a-b-c" |
nombre[0] = "J" da error. Para "modificar" un string, creas uno nuevo. Los métodos como .replace() no modifican el original: devuelven un nuevo string. Esto es una fuente de errores frecuente: olvidar guardar el resultado de un método.
⚖️ bool: verdadero, falso y los valores «truthy»
El tipo bool solo tiene dos valores posibles: True y False (con mayúscula inicial, como en inglés). Los usarás constantemente en condiciones, bucles y lógica. Pero hay algo sobre Python que no es tan obvio: casi cualquier valor puede usarse en un contexto booleano, y tiene un comportamiento «truthy» o «falsy» predefinido.
# Valores booleanos básicos activo = True inactivo = False print(type(activo)) # <class 'bool'> # bool es subclase de int en Python print(True + 1) # 2 — True vale 1 print(False + 1) # 1 — False vale 0 print(True * 5) # 5 # Valores FALSY (se comportan como False en condiciones) # 0, 0.0, "" (string vacío), [] (lista vacía), {} (dict vacío), # None, y False. Todo lo demás es TRUTHY. print(bool(0)) # False print(bool("")) # False print(bool([])) # False print(bool(42)) # True print(bool("hola")) # True print(bool([1,2])) # True # Operadores lógicos print(True and True) # True print(True and False) # False print(True or False) # True print(not True) # False
Lo de que bool sea subclase de int es uno de esos detalles de Python que te sorprenden la primera vez. True + True + True es 3. Técnicamente correcto, aunque probablemente nunca lo uses así en producción. Pero es útil saber que sum([True, False, True, True]) devuelve 2 — contar cuántos valores son verdaderos en una lista.
if nombre: es válido Python y significa «si nombre tiene algún valor». Pero cuidado: si nombre = 0 o nombre = "" o nombre = [], la condición falla aunque la variable «existe». Si quieres verificar exactamente que algo es False (y no otro valor falsy), usa if variable is False: en lugar de if not variable:.
∅ None: el valor que significa «nada»
None es el valor que representa la ausencia de valor. No es cero, no es una cadena vacía, no es False. Es literalmente «no tengo nada aquí». Lo verás constantemente: las funciones que no devuelven nada devuelven None implícitamente, y es el valor por defecto para inicializar variables que aún no tienen dato.
# None: ausencia de valor resultado = None usuario_activo = None print(type(None)) # <class 'NoneType'> # Las funciones sin return devuelven None def saludar(): print("Hola") # no hay return valor = saludar() # imprime "Hola" print(valor) # None # Comprobar None: usar "is", no "==" if resultado is None: print("No hay resultado todavía") if resultado is not None: print(f"El resultado es: {resultado}") # La diferencia entre None, 0 y "" print(None == 0) # False — None no es cero print(None == "") # False — None no es string vacío print(None == False) # False — None no es False print(None is None) # True — None solo es None
Una regla que aprendí a base de errores: siempre comprueba None con is None, no con == None. Técnicamente == None funciona casi siempre, pero hay casos donde objetos personalizados sobreescriben el operador == y pueden confundir la comparación. is None comprueba identidad de objeto, y como None es un singleton (existe una sola instancia en todo el programa), siempre es seguro.
🔄 Tipado dinámico: lo que hace diferente a Python
En lenguajes como Java o C#, cuando declaras una variable, le asignas un tipo permanente: int edad = 28;. Ese tipo no puede cambiar. Python funciona de manera radicalmente diferente: el tipo lo tiene el objeto, no la variable. La variable puede apuntar a cualquier tipo en cualquier momento.
# En Python, la misma variable puede cambiar de tipo x = 42 print(type(x)) # <class 'int'> x = "hola" print(type(x)) # <class 'str'> x = [1, 2, 3] print(type(x)) # <class 'list'> # Esto es perfectamente válido (aunque no es buen estilo) variable = True variable = 3.14 variable = "ahora soy un string" # isinstance() para comprobar el tipo de forma más pythónica numero = 42 print(isinstance(numero, int)) # True print(isinstance(numero, (int, float))) # True — comprueba varios tipos print(isinstance(numero, str)) # False
El tipado dinámico tiene ventajas (código más corto, más flexible) e inconvenientes (los errores de tipo aparecen en ejecución, no en compilación). Por eso Python 3.5+ añadió los type hints, que permiten anotar los tipos esperados sin hacer que el lenguaje sea estáticamente tipado. Los veremos en el módulo avanzado.
type(x) == int solo cuando necesites el tipo exacto. Usa isinstance(x, int) cuando quieras ser flexible con subclases. Por ejemplo, isinstance(True, int) devuelve True (porque bool es subclase de int), mientras que type(True) == int devuelve False. En la práctica, isinstance() es la forma más pythónica.
🔄 Conversión de tipos: int(), float(), str(), bool()
Python no convierte tipos automáticamente (a diferencia de JavaScript, que lo hace incluso cuando no quieres). Si necesitas cambiar el tipo de un valor, tienes que hacerlo de forma explícita. Esto se llama type casting o conversión de tipos.
# str → int texto_num = "42" numero = int(texto_num) print(numero + 8) # 50 — ahora podemos operar # str → float precio_texto = "9.99" precio = float(precio_texto) print(precio * 1.21) # 12.0879 # Número → str edad = 28 mensaje = "Tengo " + str(edad) + " años" # necesario para concatenar print(mensaje) # Mejor con f-strings (no necesitas str()) print(f"Tengo {edad} años") # más limpio # int → float (conversión implícita en operaciones) resultado = 7 / 2 # 3.5 — Python lo convierte automáticamente resultado2 = 7 // 2 # 3 — división entera, sigue siendo int # float → int (PIERDE los decimales, no redondea) print(int(3.9)) # 3, no 4 — trunca, no redondea print(round(3.9)) # 4 — round() sí redondea # Valores que NO se pueden convertir try: int("hola") # ValueError: invalid literal except ValueError as e: print(f"Error: {e}") # Conversión a bool (qué es truthy y qué es falsy) print(bool(1)) # True print(bool(0)) # False print(bool("")) # False print(bool("x")) # True
| Función | Convierte | Ejemplo | Resultado | Puede fallar si... |
|---|---|---|---|---|
int(x) | → entero | int("42") | 42 | El texto no es un entero válido |
float(x) | → decimal | float("3.14") | 3.14 | El texto no es un número válido |
str(x) | → texto | str(42) | "42" | Nunca falla |
bool(x) | → booleano | bool(0) | False | Nunca falla |
round(x, n) | Redondea float | round(3.567, 2) | 3.57 | Nunca falla |
int(3.9) devuelve 3, no 4. Siempre trunca hacia cero: int(-2.8) devuelve -2. Si quieres redondear al entero más cercano, usa round(). Si quieres redondear siempre hacia arriba, usa math.ceil(). Confundir esto genera errores sutiles que son difíciles de detectar.
✍️ Nombrar variables como un profesional
El nombre de una variable es una decisión importante. Un buen nombre hace que el código se lea casi como prosa; un mal nombre obliga a rastrear el código para entender qué hace. Estas son las convenciones de Python (definidas en el PEP 8, la guía de estilo oficial):
# ✅ snake_case para variables y funciones nombre_usuario = "Ana" precio_con_iva = 12.09 es_administrador = True numero_intentos = 0 # ✅ UPPER_CASE para constantes IVA_GENERAL = 0.21 URL_BASE = "https://api.ejemplo.com" MAX_REINTENTOS = 3 # ✅ Nombres descriptivos, no abreviaciones crípticas # MAL: n = "Ana" p = 9.99 x1 = True # BIEN: nombre = "Ana" precio = 9.99 esta_activo = True # ✅ Variables booleanas con prefijo "es_", "tiene_", "puede_" es_valido = True tiene_permisos = False puede_editar = True # ❌ Evitar # - nombres de una letra (excepto i, j, k en bucles) # - nombres genéricos como "data", "info", "temp" (sin contexto) # - mezclar español e inglés: precio_price, nombre_name # - abreviaciones que no sean estándar: usr, msg_txt, cfg
Una regla que me ayudó mucho al principio: si necesitas un comentario para explicar qué es una variable, probablemente el nombre está mal. d = 7 + comentario «número de días» es peor que dias_semana = 7 sin comentario. El nombre es la documentación.
| Estilo | Cuándo usarlo | Ejemplo |
|---|---|---|
snake_case | Variables, funciones, módulos | nombre_completo, calcular_precio() |
UPPER_SNAKE | Constantes (valores que no cambian) | MAX_CONEXIONES = 10 |
PascalCase | Nombres de clases (módulo 5) | class CuentaBancaria: |
_privado | Convención: «uso interno» | _contador_interno |
🚀 Siguiente paso: operadores
Ya sabes qué son las variables, cómo funcionan los 5 tipos fundamentales, qué es el tipado dinámico y cómo convertir entre tipos. Con esto puedes manejar cualquier dato básico en Python.
En la siguiente lección veremos los operadores en profundidad: aritméticos, de comparación, lógicos, de asignación compuesta y los operadores específicos de Python como is, in y las expresiones de asignación con :=. El operador módulo (%) que usaste en la lección anterior, la prioridad de operaciones y todas esas cosas que parecen triviales pero que cuando no las tienes claras generan bugs sutiles.
type(). Prueba a hacer conversiones entre ellas y observa qué pasa cuando la conversión no es válida. No hay mejor forma de asimilar esto que ver los mensajes de error de primera mano.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre Variables y tipos de datos en Python: la guía que ojalá hubiera tenido cuando empecé
Las dudas más comunes respondidas de forma clara y directa.
🎯 ¿Quieres certificarte en Python?
Ciberaula ofrece cursos bonificados de Python con tutor personal, desde nivel básico hasta IA y Machine Learning. Formación subvencionada por FUNDAE para trabajadores en activo.
Ver cursos de Python bonificados →💬 Foro de discusión
¿Tienes dudas sobre Variables y tipos de datos en Python: la guía que ojalá hubiera tenido cuando empecé? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!