# Variables, números y texto

URL canónica: https://missyera.com/guias/python-desde-cero/variables-y-tipos/
Última actualización: 2026-11-14
Autora: Gera Flores (Miss Yera)

> **Respuesta corta**
>
> Una variable es un nombre pegado a un valor, nada más. Yo uso cuatro tipos casi todo el tiempo: int para enteros, float para decimales, str para texto y bool para verdadero o falso. Lo que te va a pasar la primera semana es sumar un número con un texto que parecía número, y se arregla convirtiéndolo con int() o float().

Capítulo 3 de 17 del libro [Python desde cero](https://missyera.com/guias/python-desde-cero.md).

## Una variable es una etiqueta, no una caja

Hola! Vamos con lo que hay dentro de una variable, que es la base de todo lo demás 🌸

Casi siempre te van a explicar que una variable es una caja donde guardas algo. Yo prefiero otra imagen, porque te va a ahorrar un dolor de cabeza más adelante: **una variable es una etiqueta que le pegas a un valor**.

```
ciudad = 'Arequipa'
ventas = 3
promedio = 1250.75

print(ciudad, ventas, promedio)
```

```text
Arequipa 3 1250.75
```

El signo igual no significa "es igual a" como en matemáticas. Significa "desde ahora, este nombre apunta a este valor". Por eso esto que parecería una locura funciona sin problema:

```
contador = 10
contador = contador + 5

print(contador)
```

```text
15
```

Se lee de derecha a izquierda: agarra lo que hay en `contador`, súmale 5, y pega la etiqueta `contador` al resultado.

### Cómo se nombran las variables

Hay reglas que Python obliga y convenciones que obliga la comunidad. Las dos importan.

Lo que Python no te deja: empezar con un número, usar espacios, usar guiones, o usar una palabra reservada como `class` o `for`.

Lo que se acostumbra, y viene escrito en un documento que se llama PEP 8 (la guía de estilo oficial de Python):

- 🐍 Todo en minúsculas y las palabras separadas con guion bajo: `total_ventas`, `fecha_de_corte`. Eso se llama snake_case.
- 📝 Nombres que digan qué hay dentro. `df2` no dice nada; `ventas_limpias` sí.
- 🚫 Nada de tildes ni ñ, aunque Python las acepte. Te van a dar problemas al compartir código.

Te lo digo por experiencia: el código que escribes hoy lo lee otra persona dentro de tres meses, y muchas veces esa otra persona eres tú 😅

![Árbol para decidir el tipo de una variable en Python: si guarda letras es str, si guarda verdadero o falso es bool, y si guarda un número es int cuando no lleva decimales y float cuando sí. Si el dato vino de un archivo, el número también llega como str.](https://missyera.com/static/diagramas/webp/py-02-tipos.webp)

*El tipo no lo eliges tú, lo trae el dato. Y la casilla rosada es la que se lleva la primera semana de todo el mundo: un número que viene de un archivo es texto hasta que lo conviertas.*

## Los cuatro tipos que vas a usar siempre

Python tiene muchos tipos, pero para datos son cuatro los que aparecen todo el tiempo.

```
unidades = 12                 # int:   numero entero
monto = 1250.75               # float: numero con decimales
cliente = 'Minimarket El Sol' # str:   texto
pago = True                   # bool:  verdadero o falso

print(type(unidades))
print(type(monto))
print(type(cliente))
print(type(pago))
```

```text
<class 'int'>
<class 'float'>
<class 'str'>
<class 'bool'>
```

`type()` es la pregunta "qué eres tú" y la vas a usar más de lo que crees. Cuando algo no funcione y no entiendas por qué, imprime el tipo antes de seguir buscando 🔍

Un detalle sobre `bool`: por dentro es un número. Eso parece un dato curioso y en realidad es un truco que vas a usar seguido.

```
print(True + True)
print(sum([True, False, True, True]))
```

```text
2
3
```

Sumar una lista de verdaderos y falsos te da cuántos verdaderos hay. Cuando lleguemos a pandas, así vas a contar cuántas filas cumplen una condición.

## El error de la primera semana

Este te va a pasar. A todo el mundo le pasa, así que mejor que te pase aquí conmigo y no un viernes a las siete.

```
unidades = '12'      # ojo: vino como texto, con comillas
precio = 40

print(unidades * precio)
```

```text
12121212121212121212121212121212121212121212121212121212121212121212121212121212
```

Ajá 😅

Multiplicar un texto por un número no es un error para Python: es repetir el texto esa cantidad de veces. Así que te repitió "12" cuarenta veces y no te avisó de nada.

**Ese es el peor tipo de error: el que no falla.** Un error que revienta lo ves; este te deja seguir trabajando con un resultado que no significa nada.

Con la suma sí revienta, y menos mal:

```
print(unidades + precio)
```

```
TypeError: can only concatenate str (not "int") to str
```

Léelo con calma que el mensaje es bueno: "solo puedo concatenar texto con texto, y me diste un int". O sea, Python vio un texto a la izquierda y asumió que querías pegar palabras.

La solución es convertir:

```
unidades = int('12')

print(unidades + precio)
print(type(unidades))
```

```text
52
<class 'int'>
```

Las tres conversiones que vas a usar son `int()`, `float()` y `str()`. Y una advertencia que te ahorra rato: `int()` con un texto que tenga decimales no funciona.

```
int('40.5')
```

```
ValueError: invalid literal for int() with base 10: '40.5'
```

Ahí hay que pasar por `float()` primero, o directamente usar `float()` si no necesitas un entero.

## Por qué 0,1 más 0,2 no da 0,3

Este es de los que más desconciertan, y no es un bug de Python.

```
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
```

```text
0.30000000000000004
False
```

No te asustes. Pasa en todos los lenguajes, y en Excel también (solo que Excel te lo esconde al mostrarte el número).

La razón es que la computadora guarda los decimales en binario, y hay decimales que en binario no tienen representación exacta, igual que un tercio no la tiene en decimal: 0,3333... nunca termina.

Lo que importa son las dos consecuencias prácticas:

- ⚠️ **Nunca compares dos decimales con `==`.** Usa `round()` o compara la diferencia.
- 💰 **Para dinero, redondea al final** y no en cada paso. Si el proyecto es contable de verdad, existe un tipo llamado `Decimal` que guarda los números exactos.

```
print(round(0.1 + 0.2, 2) == 0.3)

from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))
```

```text
True
0.3
```

Fíjate en que `Decimal` recibe el número **entre comillas**. Si le pasas `Decimal(0.1)` le estás dando el float que ya venía roto, y no arregla nada.

## Texto, que es la mitad de tu trabajo

En datos reales, el 90% de lo sucio es texto: nombres con espacios de más, ciudades escritas de cuatro formas, números que vinieron como cadena. Así que estos métodos los vas a usar muchísimo.

```
cliente = '  minimarket EL SOL  '

print(cliente.strip())            # quita espacios de los extremos
print(cliente.strip().title())    # Primera Letra De Cada Palabra
print(cliente.strip().lower())    # todo minusculas
print(cliente.replace('EL', 'el').strip())
```

```text
minimarket EL SOL
Minimarket El Sol
minimarket el sol
minimarket el SOL
```

Un detalle importante: **estos métodos no modifican la variable, te devuelven una copia**. Si quieres conservar el cambio hay que reasignarlo.

```
cliente.strip()
print(repr(cliente))              # sigue con sus espacios

cliente = cliente.strip()
print(repr(cliente))
```

```text
'  minimarket EL SOL  '
'minimarket EL SOL'
```

Usé `repr()` para que se vean las comillas y los espacios. En la primera línea siguen ahí, aunque hayamos llamado a `.strip()` justo antes: el método devolvió una copia limpia y nadie la guardó.

Ese `repr()` va a ser tu mejor amigo depurando texto, porque un espacio de más es invisible con `print` normal 🔍

Y el que más rinde de todos, `.split()`, que parte un texto en una lista:

```
fila = 'C0045;Arequipa;Minimarket;480.37'
partes = fila.split(';')

print(partes)
print(partes[1])
print(float(partes[3]) * 2)
```

```text
['C0045', 'Arequipa', 'Minimarket', '480.37']
Arequipa
960.74
```

Eso de arriba es, en miniatura, lo que hace pandas cuando lee un CSV 📊

### f-strings: la forma de armar texto

Antes había tres maneras de meter una variable dentro de un texto y todas eran incómodas. Desde Python 3.6 hay una sola que vale la pena, y se llama f-string: una `f` antes de la comilla y las variables entre llaves.

```
cliente = 'Minimarket El Sol'
monto = 1250.756

print(f'{cliente} compró S/{monto}')
print(f'{cliente} compró S/{monto:.2f}')
print(f'{cliente:>25} | S/{monto:>10.2f}')
```

```text
Minimarket El Sol compró S/1250.756
Minimarket El Sol compró S/1250.76
        Minimarket El Sol | S/   1250.76
```

Esos dos puntos dentro de las llaves son el formato, y valen oro:

- 💵 `:.2f` deja dos decimales. Para plata, siempre.
- 📐 `:>10` alinea a la derecha en diez espacios, que es lo que hace que una tabla impresa se vea derecha.
- 🔢 `:,` pone el separador de miles: `f'{1250000:,}'` sale `1,250,000`.

El error que da la primera vez es olvidar la `f`:

```
print('{cliente} compró algo')
```

```text
{cliente} compró algo
```

No revienta, simplemente te imprime las llaves tal cual. Si ves llaves en tu salida, te faltó la `f` 🙃

## None, que no es cero ni vacío

`None` es el valor que dice "aquí no hay nada". No es el 0, no es el texto vacío, es la ausencia.

```
fecha_ultima_compra = None

print(fecha_ultima_compra is None)
print(fecha_ultima_compra == 0)
```

```text
True
False
```

Se compara con `is None` y no con `== None`. Es la convención de Python y hay razones técnicas, pero con que te acostumbres basta.

Esto importa muchísimo en datos: cuando un cliente no tiene fecha de última compra, ese hueco no es un cero. Y tratarlo como cero te cambia el promedio y todas las conclusiones que salgan de ahí.

## Lo que Python considera vacío

Cualquier valor de Python se puede usar como si fuera verdadero o falso. Y cuáles son falsos hay que sabérselo, porque de aquí sale uno de los errores más caros que vas a cometer con datos 🕳️

```
for valor in [0, 0.0, '', '0', ' ', [], {}, None, 'n/a']:
    print(f'{repr(valor):<8} -> {bool(valor)}')
```

```text
0        -> False
0.0      -> False
''       -> False
'0'      -> True
' '      -> True
[]       -> False
{}       -> False
None     -> False
'n/a'    -> True
```

Los cuatro que sorprenden 👀

- 0️⃣ **El cero es falso.** El entero y el decimal.
- 🔤 **El texto `'0'` es verdadero**, porque es un texto con un carácter dentro. El cero de mentira es verdadero y el de verdad es falso.
- ␣ **Un espacio también es verdadero.** Una celda que parece vacía en el Excel y trae un espacio, para Python tiene contenido.
- 📭 **La lista vacía y el diccionario vacío son falsos**, y eso sí es cómodo: `if resultados:` se lee "si hay resultados".

## El cero que desaparece del promedio

Ahora el error. Escribir `if descuento:` parece que dice "si vino el descuento". Lo que dice de verdad es "si el descuento no es cero ni nulo" 😖

```
descuentos = [15.0, 0.0, 8.5, None, 0.0, 12.0]

con_if = [d for d in descuentos if d]
con_is = [d for d in descuentos if d is not None]

print('con if d            :', con_if, '->', len(con_if), 'clientes')
print('con if d is not None:', con_is, '->', len(con_is), 'clientes')
print('descuento promedio, primera forma :',
      round(sum(con_if) / len(con_if), 2))
print('descuento promedio, segunda forma :',
      round(sum(con_is) / len(con_is), 2))
```

```text
con if d            : [15.0, 8.5, 12.0] -> 3 clientes
con if d is not None: [15.0, 0.0, 8.5, 0.0, 12.0] -> 5 clientes
descuento promedio, primera forma : 11.83
descuento promedio, segunda forma : 7.1
```

Mira los dos promedios. No se parecen en nada 😳

Los dos clientes con descuento **cero** son clientes reales a los que no se les dio descuento. El primer filtro los tira a la basura junto con el nulo, y el promedio sube de siete y pico a casi doce.

Nadie va a notar ese error mirando el resultado, porque once y pico de descuento promedio es un número perfectamente creíble. Y estarías reportando un 67% más de lo que es 💸

La regla, y es corta: **para preguntar si un dato vino, `is not None`. Para preguntar si un dato está vacío, `if`**. Son preguntas distintas y hay que elegir la que de verdad quieres.

## is no es lo mismo que ==

Ya usaste `is None` arriba y no te expliqué por qué no es `== None`. Aquí va, con la sorpresa incluida:

```
leido = int('2500')
escrito = 2500

print('valen lo mismo:', leido == escrito)
print('son el mismo  :', leido is escrito)

leido_chico = int('250')
escrito_chico = 250

print('con 250, valen lo mismo:', leido_chico == escrito_chico)
print('con 250, son el mismo  :', leido_chico is escrito_chico)
```

```text
valen lo mismo: True
son el mismo  : False
con 250, valen lo mismo: True
con 250, son el mismo  : True
```

Los mismos dos números, dos respuestas distintas 🤯

`==` pregunta **¿valen lo mismo?**. `is` pregunta **¿son exactamente el mismo objeto en la memoria?**, que es otra cosa.

Y el motivo de que 250 sí y 2500 no: Python guarda de antemano los enteros pequeños, de -5 a 256, y los reparte a quien los pida. Los grandes los crea nuevos cada vez. Es un detalle interno, puede cambiar entre versiones, y por eso mismo **nunca compares números ni textos con `is`**.

Con texto pasa lo mismo:

```
ciudad_a = 'Lima'
ciudad_c = ''.join(['Li', 'ma'])

print('valen lo mismo:', ciudad_a == ciudad_c)
print('son el mismo  :', ciudad_a is ciudad_c)
```

```text
valen lo mismo: True
son el mismo  : False
```

Entonces, ¿por qué `is None` sí? Porque `None` es **uno solo en todo el programa**. No hay dos. Preguntar si algo es ese objeto es la pregunta correcta, y por eso es la forma recomendada 💜

Resumido: `is` solo con `None`, `True` y `False`. Para todo lo demás, `==`.

## Redondear tampoco es lo que crees

```
print('round(0.5) =', round(0.5))
print('round(1.5) =', round(1.5))
print('round(2.5) =', round(2.5))
print('round(3.5) =', round(3.5))
```

```text
round(0.5) = 0
round(1.5) = 2
round(2.5) = 2
round(3.5) = 4
```

El 1,5 sube a 2 y el 2,5 baja a 2. No es un fallo 🧮

Se llama redondeo bancario, y la regla es: **cuando el resto es exactamente medio, se redondea al par más cercano**. Existe justamente para que los medios no se vayan todos hacia arriba y el total se infle.

```
montos = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]

print('redondeando uno a uno   :', sum(round(m) for m in montos))
print('sumando y redondeando   :', round(sum(montos)))
print('con el redondeo del cole:', sum(int(m + 0.5) for m in montos))
```

```text
redondeando uno a uno   : 50
sumando y redondeando   : 50
con el redondeo del cole: 55
```

Cincuenta contra cincuenta y cinco. Un 10% de diferencia en diez números, solo por cómo se redondea 💰

Y ahí ves para qué sirve el redondeo bancario: la mitad de los medios suben y la otra mitad bajan, así que el total se queda donde tenía que quedarse. El redondeo que te enseñaron en el colegio los sube todos y desvía el total hacia arriba.

Lo que importa en la práctica: **redondea al final, una sola vez**. Si redondeas en cada paso, cada redondeo mete su error y al final tienes un total que no cuadra con la suma de las partes. Y esa es la llamada de "el reporte no cuadra por dos soles" 📞

## Ejercicios

**1. Los cuatro tipos**

Crea una variable de cada tipo con datos de una venta e imprime el tipo de cada una en una sola línea.

```
unidades = 8
monto = 340.5
canal = 'WhatsApp'
pagado = False

print(type(unidades), type(monto), type(canal), type(pagado))
```

```text
<class 'int'> <class 'float'> <class 'str'> <class 'bool'>
```

**2. El total de una venta**

Con 8 unidades a S/34,90 cada una y 18% de IGV, imprime el total con dos decimales usando un f-string.

```
unidades = 8
precio = 34.90
igv = 0.18

total = unidades * precio * (1 + igv)
print(f'Total: S/{total:.2f}')
```

```text
Total: S/329.46
```

Fíjate en que redondeé solo al mostrar, no al calcular. Si redondeas en cada paso, los centavos se te van yendo.

**3. El texto que parecía número**

Te llega `'250'` como texto y `'1.5'` también. Multiplícalos y muestra el resultado con un decimal.

```
unidades = '250'          # como llega del sistema
factor = '1.5'            # el multiplicador de la promocion

resultado = int(unidades) * float(factor)
print(f'{resultado:.1f}')
```

```text
375.0
```

Ojo con hacer `int(factor)`: ahí revienta con un `ValueError`, porque `int()` no acepta un texto con punto decimal.

**4. Limpiar un nombre de cliente**

De `' BODEGA doña ROSA '` saca `'Bodega Doña Rosa'`.

```
sucio = '   BODEGA doña ROSA  '
limpio = sucio.strip().title()

print(repr(limpio))
```

```text
'Bodega Doña Rosa'
```

Usé `repr()` a propósito: muestra las comillas, así compruebas que de verdad no quedaron espacios. Es mi truco favorito para depurar texto 🔍

**5. Partir una fila de CSV**

De `'2026-04-29,Cusco,Web,154.83'` imprime la ciudad y el monto multiplicado por 1,18.

```
fila = '2026-04-29,Cusco,Web,154.83'
partes = fila.split(',')

print(partes[1])
print(f'{float(partes[3]) * 1.18:.2f}')
```

```text
Cusco
182.70
```

**6. La comparación que falla**

Comprueba que `1.1 + 2.2` no es igual a `3.3`, y después arregla la comparación.

```
print(1.1 + 2.2)                       # dos productos de S/1.10 y S/2.20
print(1.1 + 2.2 == 3.3)
print(round(1.1 + 2.2, 10) == 3.3)
```

```text
3.3000000000000003
False
True
```

Redondear a diez decimales suena exagerado y es justo el punto: el error del binario está mucho más allá, así que con redondear a cualquier cantidad razonable de decimales el problema desaparece.

**7. Una tabla alineada**

Imprime tres líneas con nombre de ciudad y monto, de forma que los montos queden alineados a la derecha.

```
datos = [('Lima', 12500.5), ('Arequipa', 980.25), ('Cusco', 145600.0)]

for ciudad, monto in datos:
    print(f'{ciudad:<12} S/{monto:>12,.2f}')
```

```text
Lima         S/   12,500.50
Arequipa     S/      980.25
Cusco        S/  145,600.00
```

Ese `:<12` alinea a la izquierda y `:>12,.2f` alinea a la derecha con separador de miles y dos decimales. Con eso ya puedes imprimir un reporte decente sin ninguna librería.

**8. Contar con booleanos**

Tienes una lista de montos. Cuenta cuántos superan los S/1.000 usando el truco de sumar booleanos.

```
montos = [480.37, 1250.0, 95.5, 3400.2, 780.0, 1001.0]

print(sum(m > 1000 for m in montos))
```

```text
3
```

Eso de dentro del `sum` es una comprensión, que vemos en el capítulo 4. Por ahora quédate con la idea: cada comparación devuelve True o False, y sumarlos cuenta los True.

**9. El error de tipos, provocado**

Provoca un `TypeError` sumando un texto y un número, y después arréglalo de dos formas distintas.

```
flete = '100'          # vino como texto del sistema
descuento = 50

print(int(flete) + descuento)      # los dos como numero
print(flete + str(descuento))      # los dos como texto
```

```text
150
10050
```

Las dos son válidas y hacen cosas muy distintas. Elegir mal aquí es de donde salen los reportes con números absurdos.

**10. None no es cero**

Simula tres clientes donde uno no tiene fecha de última compra, y cuenta cuántos sí la tienen.

```
fechas = ['2025-08-22', None, '2026-01-05']

con_fecha = sum(f is not None for f in fechas)
print(f'{con_fecha} de {len(fechas)} tienen fecha')
```

```text
2 de 3 tienen fecha
```

Este conteo es lo primero que haces al recibir cualquier archivo. En el capítulo 12 pandas te lo da en una línea, pero es exactamente la misma idea.

**11. Cuáles de estas celdas están vacías de verdad**

Clasifica una fila de un Excel real, donde "vacío" viene de cinco formas distintas.

```
celdas = {'ciudad': 'Lima', 'descuento': 0.0, 'nota': '',
          'telefono': '   ', 'satisfaccion': None, 'codigo': '0'}

for nombre, valor in celdas.items():
    print(f'{nombre:<13} {repr(valor):<8} '
          f'vacio para Python: {not valor!s:<6} '
          f'falta el dato: {valor is None}')
```

```text
ciudad        'Lima'   vacio para Python: False  falta el dato: False
descuento     0.0      vacio para Python: True   falta el dato: False
nota          ''       vacio para Python: True   falta el dato: False
telefono      '   '    vacio para Python: False  falta el dato: False
satisfaccion  None     vacio para Python: True   falta el dato: True
codigo        '0'      vacio para Python: False  falta el dato: False
```

Fíjate en `telefono` y en `codigo`, que son los dos que mienten en direcciones opuestas 🎭

El teléfono son tres espacios: para Python tiene contenido, para ti está vacío. El código es el texto `'0'`: para Python tiene contenido y para ti también, pero si lo conviertes a número se vuelve falso.

Por eso, con texto que viene de un Excel, el `.strip()` va **antes** de preguntar si está vacío. Siempre.

**12. El promedio que se infla solo**

Calcula la satisfacción promedio filtrando de las dos maneras y mira la diferencia.

```
notas = [5, 0, 4, None, 0, 3, 5, None, 1]

filtrado_mal = [n for n in notas if n]
filtrado_bien = [n for n in notas if n is not None]

print('respuestas contadas, mal :', len(filtrado_mal))
print('respuestas contadas, bien:', len(filtrado_bien))
print('promedio mal :', round(sum(filtrado_mal) / len(filtrado_mal), 2))
print('promedio bien:', round(sum(filtrado_bien) / len(filtrado_bien), 2))
print('inflado en   :',
      round((sum(filtrado_mal) / len(filtrado_mal))
            / (sum(filtrado_bien) / len(filtrado_bien)) * 100 - 100, 1), '%')
```

```text
respuestas contadas, mal : 5
respuestas contadas, bien: 7
promedio mal : 3.6
promedio bien: 2.57
inflado en   : 40.0 %
```

Dos clientes pusieron cero, o sea la peor nota posible, y el primer filtro los borra del mapa 😤

El resultado es una satisfacción promedio bastante mejor de la real, calculada sobre menos gente de la que contestó. Y todo por una letra de diferencia al escribir el filtro.

**13. Dos números iguales que no son el mismo**

Comprueba dónde `is` y `==` se separan.

```
pares = [('2500', 2500), ('250', 250), ('7', 7), ('100000', 100000)]

for texto, numero in pares:
    leido = int(texto)
    print(f'{texto:>7}  ==  {leido == numero}   is  {leido is numero}')
```

```text
   2500  ==  True   is  False
    250  ==  True   is  True
      7  ==  True   is  True
 100000  ==  True   is  False
```

Los dos de en medio dicen `True` y los de los extremos `False`, y el corte está entre 256 y 257 🚧

Ese es el rango que Python se guarda de antemano. Y lo que quiero que te lleves no es el número exacto: es que **un resultado que depende de si el número es grande o pequeño no puede ser la base de nada**. Con `==` las cuatro líneas darían `True`, que es lo que querías preguntar.

**14. Redondear y cortar no son lo mismo**

Calcula el IGV de mil facturas y suma el total de tres maneras: exacta, redondeando cada factura, y cortando los decimales de cada factura.

```
base = [round(3 + i * 0.7137, 3) for i in range(1000)]
con_igv = [m * 1.18 for m in base]

exacto = sum(con_igv)
redondeado = sum(round(m, 2) for m in con_igv)
truncado = sum(int(m * 100) / 100 for m in con_igv)

print('exacto     : S/', round(exacto, 2))
print('redondeando: S/', round(redondeado, 2),
      ' se desvia', round(redondeado - exacto, 2))
print('truncando  : S/', round(truncado, 2),
      ' se desvia', round(truncado - exacto, 2))
print('el truncado se come, por factura: S/',
      round((exacto - truncado) / len(base), 4))
```

```text
exacto     : S/ 424201.92
redondeando: S/ 424201.91  se desvia -0.01
truncando  : S/ 424196.99  se desvia -4.93
el truncado se come, por factura: S/ 0.0049
```

Y aquí la sorpresa, que va en contra de lo que yo esperaba antes de medirlo 🌸

**Redondear en cada factura casi no desvía nada**: un céntimo en mil facturas. Porque la mitad de los redondeos suben y la otra mitad bajan, y se compensan solos.

**Truncar sí desvía**, y bastante: casi cinco soles en las mismas mil facturas. `int(m * 100) / 100` corta los decimales, así que **siempre baja, nunca sube**. Medio céntimo por factura que va siempre en la misma dirección, y eso no se compensa con nada.

La lección no es "nunca redondees en medio". Es esta: **lo que hace daño no es el error, es el sesgo**. Un error que se reparte para los dos lados se cancela. Uno que siempre va hacia el mismo lado se acumula, factura a factura, hasta que alguien pregunta por qué el reporte no cuadra 📞

Aun así, la costumbre buena sigue siendo redondear solo al mostrar. El redondeo es una decisión de presentación, no de cálculo 🎁

## Dos números iguales que Python dice que no lo son

Antes de cerrar los tipos, una que parece un detalle de nada y se lleva tardes enteras. Léela despacio, porque el código de abajo no da error: da una respuesta 🧐

La trampa

Comparas dos códigos de cliente con `is`, que se lee mejor que `==`. Lo pruebas con los primeros del archivo y funciona.

```
a = int('256')
b = int('256')
print(a is b)      # True

a = int('257')
b = int('257')
print(a is b)      # False
```

**Qué está mal**

Los dos son iguales y solo uno da True 😬 `is` no pregunta si valen lo mismo: pregunta si son **el mismo objeto en memoria**. Y Python guarda cacheados los enteros del -5 al 256, así que hasta ahí coinciden por casualidad.

De 257 en adelante, dos números iguales son objetos distintos y `is` dice False. O sea que tu comparación funciona mientras pruebas con datos pequeños y empieza a fallar en producción, en silencio y solo con algunos clientes.

La regla no tiene excepciones: **`==` para comparar valores, `is` solo con `None`**. `is None` está bien porque None es único de verdad.

Comprueba que lo tienes

Lees un CSV y al sumar dos columnas te sale un TypeError. ¿Qué pasó?

- Una de las dos es texto aunque parezca un número
- Hay un valor nulo en medio
- Los números son demasiado grandes
- Falta convertir a float

## Lo que te llevas

- 🏷️ Una variable es una etiqueta pegada a un valor, y el `=` se lee de derecha a izquierda.
- 🔍 Cuando algo raro pase, `type()` antes que nada.
- 💸 Los decimales no son exactos: no los compares con `==` y redondea al final.
- ✍️ Los métodos de texto devuelven una copia, no modifican la variable.
- 🕳️ `None` es ausencia, no cero, y confundirlos te cambia los promedios.

Y si de todo el capítulo te llevas una sola frase, que sea esta:

El tipo no es un detalle del lenguaje. Es la diferencia entre sumar y pegar.

Si vienes de Excel esto ya lo sufriste: la celda que se alinea a la izquierda porque el número entró como texto. Es el mismo problema y está contado desde ahí en la [guía de Excel](https://missyera.com/guias/excel-desde-cero/) 📊

En el capítulo 4 vamos a guardar muchas cosas juntas: listas, tuplas y diccionarios. Ahí aparece el error de copiar una lista que en realidad no se copió, que es de los más traicioneros que hay.

Que tengas lindo día! 🌸

## Seguir leyendo

- Anterior: [Instalar Python y correr lo primero](https://missyera.com/guias/python-desde-cero/instalar-y-empezar.md)
- Siguiente: [Listas, tuplas y diccionarios](https://missyera.com/guias/python-desde-cero/listas-y-diccionarios.md)

## Contacto

- Diagnóstico de 30 minutos con Miss Yera: https://calendly.com/soymissyera/reunion-con-yera
- Correo: hola@missyera.com
- Esta página en HTML: https://missyera.com/guias/python-desde-cero/variables-y-tipos/
- Índice de todas las páginas en texto: https://missyera.com/llms-index.txt
