symtable en Python: ámbitos y símbolos

Publicado el: 03/08/2026
Tempo de leitura: 7 minutos
Desarrollador analizando estructura de código y tablas de símbolos con symtable en Python

Antes de generar bytecode, el compilador de Python recorre el árbol sintáctico y decide el ámbito de cada identificador. Debe determinar si un nombre es local, global, nonlocal, parámetro, importado, variable libre o namespace. El módulo symtable en Python expone estas tablas de símbolos para análisis estático, herramientas educativas, linters, generadores de documentación y estudios sobre closures.

Esta guía muestra cómo crear una tabla con symtable(), recorrer funciones y clases, consultar símbolos y reconocer variables libres, globals, annotations, comprehensions y type parameters. Complementa nuestros artículos sobre bytecode con dis, introspección con inspect, descriptors, singledispatch y tracebacks.

El papel de una tabla de símbolos

Un árbol de sintaxis abstracta describe funciones, asignaciones y llamadas, pero el compilador todavía debe resolver el significado de cada nombre dentro de su bloque. Las tablas se generan después del AST y antes del bytecode.

Determinan, por ejemplo, que un nombre asignado dentro de una función es local, salvo que una declaración global o nonlocal cambie su clasificación. También identifican variables libres capturadas por closures.

Crear una tabla

symtable.symtable() recibe código fuente, un nombre de archivo utilizado en mensajes y un modo de compilación.

import symtable

codigo = """
tasa = 0.1

def total(valor):
    return valor * (1 + tasa)
"""

tabla = symtable.symtable(
    codigo,
    "ejemplo.py",
    "exec",
)

print(tabla.get_name())
print(tabla.get_type())

Los modos siguen a compile(): exec para módulos y bloques, eval para expresiones y single para una instrucción interactiva.

Tipos de tabla

get_type() devuelve un miembro de SymbolTableType. Los tipos centrales representan módulos, funciones y clases. Python moderno también incluye ámbitos de annotations, aliases de tipo, parámetros de tipo y variables de tipo.

from symtable import SymbolTableType

if tabla.get_type() is SymbolTableType.MODULE:
    print("Tabla del módulo")

La documentación oficial de symtable recomienda utilizar el enum en lugar de strings fijas porque los valores textuales pueden cambiar.

Identificadores del módulo

get_identifiers() devuelve los nombres reconocidos en el bloque.

print(list(tabla.get_identifiers()))
# ['tasa', 'total']

get_symbols() devuelve objetos Symbol con flags detalladas.

for simbolo in tabla.get_symbols():
    print(
        simbolo.get_name(),
        simbolo.is_local(),
        simbolo.is_global(),
        simbolo.is_namespace(),
    )

En el nivel del módulo, los nombres asignados suelen ser locales al módulo y globales para el programa.

Consultar un nombre

lookup() recupera la entrada de un identificador.

simbolo = tabla.lookup("total")
print(simbolo.is_assigned())
print(simbolo.is_namespace())

Un nombre introducido por una definición de función o clase es un namespace. El mismo nombre puede estar asociado con varios namespaces si el código lo vuelve a enlazar.

Recorrer tablas hijas

Funciones, clases y otros ámbitos anidados aparecen en get_children().

for hija in tabla.get_children():
    print(
        hija.get_name(),
        hija.get_type(),
        hija.get_lineno(),
    )

has_children() informa si existen namespaces internos, mientras is_nested() identifica una función o clase anidada.

Analizar una función

Las tablas de función ofrecen métodos para parámetros, locales, globals, nonlocals y variables libres.

funcion = tabla.get_children()[0]

print(funcion.get_parameters())
print(funcion.get_locals())
print(funcion.get_globals())
print(funcion.get_nonlocals())
print(funcion.get_frees())

En el ejemplo, valor es parámetro y local. tasa es global porque la función la lee sin asignarla localmente.

Globals implícitos

Considera una lectura sin asignación local.

codigo = """
configuracion = {}

def obtener():
    return configuracion
"""

En la tabla de la función, configuracion es global. Esto no prueba que el nombre exista durante la ejecución; clasifica cómo se compila la búsqueda.

Declaraciones global explícitas

is_declared_global() diferencia una instrucción global de una búsqueda global implícita.

codigo = """
contador = 0

def incrementar():
    global contador
    contador += 1
"""

tabla = symtable.symtable(codigo, "contador.py", "exec")
funcion = tabla.get_children()[0]
simbolo = funcion.lookup("contador")

print(simbolo.is_global())
print(simbolo.is_declared_global())

Un linter puede usar esta diferencia para señalar mutaciones de estado global.

nonlocal y closures

nonlocal permite modificar una variable de una función externa.

codigo = """
def crear_contador():
    valor = 0

    def incrementar():
        nonlocal valor
        valor += 1
        return valor

    return incrementar
"""

La tabla interna clasifica valor como nonlocal y libre.

raiz = symtable.symtable(codigo, "closure.py", "exec")
externa = raiz.get_children()[0]
interna = externa.get_children()[0]

print(interna.get_nonlocals())
print(interna.get_frees())

Esto explica por qué el compilador crea celdas accesibles mediante opcodes como LOAD_DEREF.

Flags de símbolos

Un Symbol expone varios predicados:

  • is_referenced(): usado en su bloque;
  • is_assigned(): asignado en el bloque;
  • is_parameter(): parámetro de función;
  • is_imported(): creado por import;
  • is_local(), is_global() e is_nonlocal();
  • is_free(): resuelto en un ámbito externo;
  • is_annotated(): posee annotation;
  • is_namespace(): introduce un namespace.

Combina flags en lugar de asumir que todas son mutuamente excluyentes.

Imports

is_imported() identifica nombres creados por instrucciones import.

codigo = """
import json
from pathlib import Path
"""

tabla = symtable.symtable(codigo, "imports.py", "exec")

for nombre in tabla.get_identifiers():
    simbolo = tabla.lookup(nombre)
    print(nombre, simbolo.is_imported())

Esto ayuda a analizadores de dependencias. Los imports dinámicos con importlib o __import__() no aparecen como enlaces estáticos normales.

Annotations

is_annotated() informa si un nombre posee annotation.

codigo = """
cantidad: int
precio: float = 10.0
"""

tabla = symtable.symtable(codigo, "tipos.py", "exec")

for nombre in tabla.get_identifiers():
    print(nombre, tabla.lookup(nombre).is_annotated())

Una annotation y un valor asignado son hechos diferentes. Un nombre puede estar anotado sin recibir un valor en esa línea.

Type parameters en Python moderno

Python 3.12 introdujo sintaxis de parámetros de tipo y ámbitos relacionados. Python 3.14 añade is_type_parameter().

codigo = """
def primero[T](elementos: list[T]) -> T:
    return elementos[0]
"""

tabla = symtable.symtable(codigo, "generics.py", "exec")

El parser debe soportar la sintaxis. Las herramientas deberían detectar la versión y usar SymbolTableType.TYPE_PARAMETERS.

Clases y métodos

Las tablas de clase heredan de SymbolTable. get_methods() lista funciones declaradas directamente en el cuerpo, pero fue deprecado en Python 3.14 y está previsto eliminarlo en Python 3.16.

Las herramientas nuevas deberían recorrer get_children(), seleccionar tablas de función y considerar ámbitos de parámetros de tipo que pueden aparecer entre la clase y sus métodos.

Nombres de clase y variables libres

Python 3.14 añade is_free_class() para un nombre de clase que es libre desde la perspectiva de un método.

codigo = """
def externa():
    x = 1
    class C:
        x = 2
        def metodo(self):
            return x
"""

El método devuelve el x de la función externa, no el atributo del cuerpo de la clase. Los bloques de clase no actúan como ámbitos léxicos de función.

Comprehensions

Python 3.14 añade is_comp_iter() para variables de iteración y is_comp_cell() para celdas de comprehensions inlined.

codigo = """
def cuadrados(valores):
    return [valor * valor for valor in valores]
"""

La forma exacta de la tabla depende de optimizaciones de la versión. Usa flags públicas en vez de nombres internos de bloques generados.

Almacenamiento optimizado

is_optimized() indica si los locales pueden usar almacenamiento optimizado. Las funciones suelen utilizar fast locals, mientras los módulos usan mappings de namespace.

Esta flag ayuda a explicar diferencias en el comportamiento de locals() dentro de módulos y funciones optimizadas.

IDs y líneas

get_id() devuelve un identificador de tabla y get_lineno() la primera línea del bloque.

for hija in tabla.get_children():
    print(hija.get_id(), hija.get_lineno())

No persistas get_id() como clave estable. Para reportes, combina archivo, tipo, nombre y línea.

Uso desde la terminal

Desde Python 3.13, el módulo puede ejecutarse como script.

python -m symtable programa.py

Sin archivos, lee la entrada estándar. La salida sirve para exploración; las aplicaciones deberían usar la API.

symtable frente a ast

La documentación oficial de AST describe nodos sintácticos y permite localizar asignaciones, llamadas y expresiones. symtable añade la clasificación de ámbito del compilador.

Un linter robusto suele usar ambos: AST para el contexto de la instrucción y la tabla para saber a qué namespace pertenece el nombre.

symtable frente a inspect

symtable trabaja con código fuente sin ejecutarlo. inspect examina objetos vivos después del import o la ejecución. El análisis estático reduce riesgos con código desconocido, aunque todavía deben existir límites para entradas enormes.

Ejemplo de reporte

def describir(tabla, ruta=()):
    actual = ruta + (tabla.get_name(),)

    for simbolo in tabla.get_symbols():
        yield {
            "ambito": ".".join(actual),
            "nombre": simbolo.get_name(),
            "local": simbolo.is_local(),
            "global": simbolo.is_global(),
            "nonlocal": simbolo.is_nonlocal(),
            "libre": simbolo.is_free(),
            "parametro": simbolo.is_parameter(),
            "importado": simbolo.is_imported(),
        }

    for hija in tabla.get_children():
        yield from describir(hija, actual)

El resultado puede serializarse como JSON o mostrarse en una herramienta educativa.

Limitaciones

El módulo no revela tipos ni valores de runtime, branches ejecutados o imports dinámicos. Sigue las reglas del compilador de la versión actual. Código con sintaxis más reciente puede generar SyntaxError.

El análisis de proyectos completos también debe resolver archivos, paquetes, imports, stubs, código generado y configuración del type checker.

Errores frecuentes

  • Asumir que un global existirá durante la ejecución.
  • Tratar todas las flags como mutuamente excluyentes.
  • Comparar strings de tipo en lugar del enum.
  • Depender de get_methods(), ya deprecado.
  • Ignorar ámbitos de annotations y type parameters.
  • Suponer que una clase cierra nombres como una función.
  • Ejecutar código cuando basta un análisis estático.
  • Persistir IDs internos como identificadores estables.

Buenas prácticas

  • Usa SymbolTableType.
  • Recorre todas las tablas hijas.
  • Combina symtable con AST.
  • Registra versión y archivo.
  • Prueba globals, nonlocals, closures y comprehensions.
  • Considera ámbitos modernos de tipado.
  • Evita helpers deprecados.
  • Limita tamaño y complejidad de fuentes no confiables.

Conclusión

El módulo symtable en Python abre una ventana a la fase del compilador que resuelve identificadores y ámbitos. Distingue parámetros, locales, globals, nonlocals, imports, namespaces, annotations y variables libres antes de generar bytecode.

Esta información resulta valiosa para linters, herramientas educativas, documentación y análisis de closures. Combinando tablas de símbolos con AST y considerando nuevos ámbitos de tipado y comprehensions, una herramienta puede comprender el significado de los nombres sin ejecutar el programa ni reimplementar reglas complejas del compilador.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Teclado internacional que representa números, moneda y fechas con locale en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    locale en Python: números, moneda y fechas

    Aprende locale en Python para formatear e interpretar números, moneda, fechas, encodings y orden cultural sin errores de concurrencia.

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Monitor y red que representan información del sistema con platform en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    platform en Python: información del sistema

    Aprende platform en Python para identificar sistema operativo, arquitectura, distribución, versión de Python y entorno de ejecución.

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Código y compilador que representan rutas y variables de build con sysconfig en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sysconfig en Python: rutas y build

    Aprende sysconfig en Python para descubrir rutas de instalación, variables de build, headers, virtualenvs y plataformas de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Disco duro que representa archivos mapeados en memoria con mmap en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mmap en Python: archivos en memoria

    Aprende mmap en Python para mapear archivos en memoria, buscar bytes, compartir datos y elegir lectura, escritura o copy-on-write.

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Código fuente que representa tokens y constantes del parser con el módulo token en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    token en Python: constantes del parser

    Aprende token en Python para interpretar tipos léxicos, operadores exactos, indentación, f-strings, t-strings y parsers por versión.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Código fuente que representa palabras reservadas y soft keywords en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    keyword en Python: palabras reservadas

    Aprende keyword en Python para validar identificadores, palabras reservadas y soft keywords según la versión del intérprete.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026