Python y Excel 365: el operador @ en fórmulas exportadas con openpyxl (solución con xlwings)
Problema frecuente para quienes automatizan Excel con Python: al exportar fórmulas usando openpyxl, Excel 365 añade el operador @ delante de las funciones (por ejemplo, @SI en vez de SI). Esto ocurre porque openpyxl no tiene soporte nativo para matrices dinámicas, y Excel 365 interpreta las fórmulas en modo clásico aplicando la intersección implícita (el operador @ fuerza que solo se muestre el primer resultado).
La raíz técnica es la diferencia entre Formula (modo clásico, que openpyxl usa) y Formula2 (modo moderno con matrices dinámicas y desbordamiento natural). A partir de Excel 365, las fórmulas que podrían desbordar se limitan con @ si se escriben en modo clásico.
La comunidad propuso usar xlwings, que permite acceder a la API COM de Excel y escribir fórmulas con Formula2. Se compartió un script Python completo y bien documentado que detecta si el archivo ya está abierto en Excel (para no abrir una instancia duplicada), escribe fórmulas usando Formula2 con fallback a Formula clásico, y gestiona correctamente el ciclo de vida de la instancia Excel:
``python
Con xlwings, usar Formula2 para modo moderno (matrices dinámicas)
try:
rng.api.Formula2 = formula # Modo moderno
except Exception:
rng.api.Formula = formula # Fallback al modo clásico
`
El trade-off es rendimiento: openpyxl procesa en ~30 segundos (trabaja directamente con el XML del xlsx), mientras que xlwings tarda ~4-5 minutos (usa COM, necesita una instancia de Excel corriendo). Pero la ventaja de xlwings es que las fórmulas matriciales y dinámicas funcionan correctamente sin el operador @.
Funciones y conceptos clave: Formula2 vs Formula (API COM), operador de intersección implícita (@), matrices dinámicas y desbordamiento (spill), SEQUENCE, SUM, TAKE con referencias desbordadas (F1#`).
El problema: Excel te mete un arroba que tú no escribiste
Automatizas la generación de un Excel con Python, escribes las fórmulas con openpyxl, abres el fichero... y te encuentras con que Excel ha metido un arroba delante de tus funciones. Donde tú pusiste una función condicional, ahora hay un arroba y luego la función. Y las fórmulas que deberían derramar devuelven un único valor.
No es un bug de openpyxl ni un capricho de Excel. Es el operador de intersección implícita, y entender por qué aparece es lo que te permite quitarlo de en medio.
Por qué pasa: Formula frente a Formula2
Excel guarda las fórmulas de dos maneras distintas, y la diferencia es histórica.
- El modo clásico es el de toda la vida, anterior a las matrices dinámicas. En ese modo, una fórmula ocupa una celda y devuelve un valor. Si la expresión pudiera generar varios resultados, Excel aplica la intersección implícita y se queda con el que corresponde a la fila actual.
- El modo moderno es el que llegó con las matrices dinámicas. Aquí una fórmula puede devolver una matriz entera y derramarse por las celdas de al lado sin pedir permiso.
openpyxl trabaja directamente sobre el XML del fichero xlsx y escribe en el atributo del modo clásico, porque no tiene soporte nativo para matrices dinámicas. Cuando Excel 365 abre ese fichero y ve una fórmula en modo clásico que podría derramar, hace lo que le dicta la compatibilidad: le pone el arroba delante para forzar que devuelva un solo valor.
Dicho de otro modo: el arroba no es un error, es Excel siendo retrocompatible con un fichero que le llega marcado como antiguo.
La solución: xlwings y la API COM
La comunidad apuntó al mismo sitio: si el problema es que openpyxl solo puede escribir en el atributo clásico, hay que escribir en el moderno. Y para eso hace falta hablar con Excel de verdad, no con el XML.
xlwings hace exactamente eso: se conecta a la API COM de Excel, lo que da acceso a la propiedad del modo moderno. El patrón es tan simple como esto:
# Intenta el modo moderno (matrices dinámicas)
try:
rng.api.Formula2 = formula
except Exception:
rng.api.Formula = formula # Fallback al modo clásicoEl try con except no es paranoia: en versiones antiguas de Excel la propiedad moderna no existe, y ahí el fallback al modo clásico salva la ejecución.
En el grupo se compartió un script completo y bien documentado que va más allá del snippet y resuelve los detalles feos de trabajar con COM:
- Detecta si el fichero ya está abierto en Excel, para engancharse a esa instancia en lugar de abrir una duplicada. Si no lo haces, acabas con dos Excel peleándose por el mismo archivo.
- Escribe con la propiedad moderna y cae a la clásica si no está disponible.
- Gestiona el ciclo de vida de la instancia: cerrar lo que se abrió, dejar abierto lo que ya estaba. Un proceso de Excel huérfano en segundo plano bloquea el fichero y no te enteras hasta el siguiente intento.
El precio: rendimiento
Esto no sale gratis, y conviene saberlo antes de migrar todo el pipeline.
- openpyxl: unos 30 segundos. Trabaja directamente sobre el XML comprimido del xlsx, sin abrir Excel.
- xlwings: unos 4 o 5 minutos. Necesita una instancia real de Excel corriendo y cada escritura viaja por COM.
Es un orden de magnitud de diferencia. La decisión práctica suele ser mixta: usa openpyxl para el grueso de los datos y las fórmulas sencillas que nunca van a derramar, y reserva xlwings para el puñado de celdas donde de verdad necesitas matrices dinámicas.
Conceptos clave
- Intersección implícita (el operador arroba) — fuerza que una expresión devuelva un solo valor en lugar de una matriz. Si aparece sin que lo escribas, es señal de que la fórmula está guardada en modo clásico.
- Modo clásico frente a modo moderno — dos formas de guardar la misma fórmula en el fichero. openpyxl solo escribe la primera; xlwings puede escribir la segunda.
- Derramamiento — el comportamiento por el que una fórmula ocupa las celdas contiguas necesarias para mostrar toda su matriz.
- Referencias a rangos derramados — el sufijo de almohadilla que apunta a toda la matriz generada por otra celda, sin tener que saber su tamaño.
- API COM — la interfaz que permite controlar Excel como aplicación desde otro programa, y la única vía para tocar propiedades que el XML no expone.
Conclusión
El arroba fantasma es de esos problemas que parecen un fallo de la librería y resultan ser una diferencia de formato de fichero. Una vez sabes que existen dos maneras de guardar una fórmula, la solución se elige sola: si tu automatización genera fórmulas que deben derramar, necesitas escribir en modo moderno, y eso hoy pasa por xlwings.
Si tu caso es el contrario, fórmulas simples de una sola celda, openpyxl sigue siendo la opción sensata por velocidad. Casos como este salen a menudo en la comunidad de InflueXcel, donde la parte de Excel y la de Python se cruzan más de lo que parece.
Más contenido de Excel en InflueXcel
- Acumular el saldo en PIVOTARPOR: una función de agregación distinta por columna CasoNuevo caso que empieza con una suposicion razonable y acaba en una contraprueba del propio autor. Juan trae un libro mayor en bruto y quiere
- CONTAR.SI no acepta ELEGIRCOLS: por qué las funciones .SI exigen una referencia y no una matriz CasoHay errores de Excel que te mandan a buscar en la dirección equivocada, y este es de manual. Joan Recasens llega al grupo con una fórmula qu
- Dar formato a la última fila de una tabla que crece (y el techo del formato condicional) CasoUn miembro de la comunidad llega con una tabla cuyo rango real va de la columna embalaje a la columna total, y cuya columna de numeración co
- Colorear la fila entera según el técnico asignado: la referencia mixta que casi nadie aplica bien CasoEsta semana surgió una duda que parece de principiante y en realidad esconde el concepto peor entendido del formato condicional. Johann Frar
- Del VBA a una sola fórmula: calcular el RFC mexicano con LET, MAP y expresiones regulares CasoHector llega al grupo con un problema que no se ve todos los días: tiene resuelto el cálculo del RFC mexicano, pero lo tiene en VBA, y quier
- Un rango desbordado como serie de un gráfico: el nombre tiene que ser de ámbito Hoja CasoMiki llega al grupo con un caso de esos que no dan error, simplemente no funcionan. Quiere un gráfico que crezca solo: si mañana hay más dat
- Cuenta clientes y cervezas en Excel 🍺 Caso "La Taberna: El Poney Pisador" (Nivel 1) Tutorial🍺 Noche cerrada en Bree. Frodo, Sam, Merry y Pippin cruzan la puerta de El Poney Pisador huyendo de los Jinetes Negros: la sala está a reven
- SUMAR.SI.CONJUNTO en acción con El Señor de los Anillos 🃏 El 21 de La Comarca TutorialLo que practicamos en este caso: • Contar cartas por palo con CONTAR.SI • Sumar valores con condiciones (SUMAR.SI / SUMAR.SI.CONJUNTO) • Apl
- Resolver ecuaciones iterativas en Excel: Colebrook con LAMBDA recursiva y REDUCE CasoCaso excepcional que demuestra el poder de las funciones modernas de Excel en ingeniería. Un ingeniero industrial de casi 70 años, consultor
- Nueva Función IMPORTTEXT TutorialNueva función IMPORTTEXT en Excel!! https://techcommunity.microsoft.com/blog/microsoft365insiderblog/bring-data-into-excel-with-the-new-impo