symtable en Python: analiza scopes

Publicado el: 27/08/2026
Tempo de leitura: 9 minutos
A detailed image of a reticulated python showcasing its patterned scales and intricate skin texture.

El módulo symtable expone las tablas de símbolos producidas por el compilador de Python antes de generar bytecode. Permite saber qué nombres pertenecen a cada scope, cuáles son parámetros, imports, variables locales, globals, nonlocals, free variables, referencias, asignaciones y namespaces anidados. Esta capa es útil en linters, refactorings, herramientas educativas, análisis estático e inspección de closures.

Una symbol table no ejecuta el programa ni resuelve todos los comportamientos dinámicos. Asignaciones mediante globals(), setattr(), imports dinámicos, decorators, metaclasses y monkey patching quedan fuera de una comprensión completa. Aun así, la tabla refleja las decisiones reales de scope léxico tomadas por el compilador y es más fiable que inferir bindings solo a partir del texto.

Crea una tabla de símbolos

symtable.symtable() recibe el código fuente, un filename y un modo de compilación.

import symtable

codigo = """
x = 10

def sumar(y):
    z = x + y
    return z
"""

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

El modo puede ser exec, eval o single, igual que en compile().

Errores de sintaxis

La construcción utiliza el compilador y puede lanzar SyntaxError.

try:
    tabla = symtable.symtable(codigo, ruta, "exec")
except SyntaxError as error:
    reportar(ruta, error.lineno, error.offset, error.msg)

Al analizar un proyecto, registra el error de un archivo y continúa con los demás.

La tabla principal

La tabla raíz representa el módulo, expresión o entrada interactiva.

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

El nombre y la línea ayudan a generar diagnósticos, aunque su significado exacto depende del tipo y de la versión de Python.

Lista los identificadores

get_identifiers() devuelve los nombres conocidos en un scope.

for nombre in sorted(tabla.get_identifiers()):
    print(nombre)

El resultado incluye bindings y referencias relevantes para el compilador, no únicamente nombres a la izquierda de una asignación.

Inspecciona un nombre con lookup

lookup(name) devuelve un objeto Symbol con información de clasificación.

simbolo = tabla.lookup("x")
print(simbolo.is_global())
print(simbolo.is_assigned())
print(simbolo.is_referenced())

Consultar un nombre inexistente puede fallar, por lo que conviene comprobar primero los identificadores o manejar la excepción.

Variables locales

is_local() indica que un nombre pertenece al scope local actual.

tabla_funcion = tabla.lookup("sumar").get_namespace()
for nombre in tabla_funcion.get_identifiers():
    simbolo = tabla_funcion.lookup(nombre)
    if simbolo.is_local():
        print("local", nombre)

Los parámetros también son locales, pero disponen de la clasificación específica is_parameter().

Parámetros

Las tablas de funciones exponen directamente los nombres de parámetros.

for nombre in tabla_funcion.get_parameters():
    print(nombre)

Esta información ayuda a detectar parámetros no usados, shadowing, problemas de nombres y argumentos exigidos por una interfaz.

Globals implícitos y declarados

Un nombre puede ser global porque no existe binding local o porque aparece una declaración global.

contador = 0

def incrementar():
    global contador
    contador += 1

Usa is_global() y, cuando esté disponible, is_declared_global() para diferenciar los casos relevantes.

Nombres nonlocal

nonlocal vincula un nombre a un scope de función exterior, no al módulo.

def externa():
    total = 0

    def interna():
        nonlocal total
        total += 1
        return total

    return interna

El símbolo interior puede reconocerse con is_nonlocal().

Free variables

Una free variable es leída por una función interna y proporcionada por un scope exterior.

tabla_interna = tabla_externa.get_children()[0]
print(tabla_interna.get_frees())

Estos nombres participan en la creación de closures y más tarde aparecen en los metadatos del code object.

Cell variables

Cuando una variable local es capturada por una función anidada, el compilador necesita guardarla en una cell. La relación puede inferirse comparando los locals del scope exterior con los frees de sus hijos.

Después de compilar, los code objects muestran información relacionada mediante co_cellvars y co_freevars. Usa symtable para el nivel de fuente y code objects para inspección posterior.

Nombres importados

is_imported() indica que un nombre fue introducido mediante import.

import json as serializador
from pathlib import Path

El alias local, y no necesariamente el nombre original del módulo, es el binding que aparece en la tabla.

Asignaciones

is_assigned() reconoce nombres que reciben un binding en el scope.

Las asignaciones incluyen más que el operador =: targets de loops, imports, definiciones, exception targets, comprehensions y capturas de patterns también crean bindings. Combina symtable con AST si necesitas clasificar el origen.

Referencias

is_referenced() indica que el nombre se usa en una expresión u operación relevante.

Una variable asignada pero nunca referenciada puede merecer un aviso, pero considera APIs públicas, firmas de callbacks, decorators, efectos laterales y convenciones como el prefijo underscore.

Namespaces anidados

Un símbolo de función o clase puede poseer una o varias tablas hijas.

simbolo = tabla.lookup("sumar")
if simbolo.is_namespace():
    namespace = simbolo.get_namespace()

get_namespaces() resulta útil cuando un mismo nombre de fuente puede corresponder a varios namespaces en construcciones compatibles.

Recorre las tablas hijas

get_children() devuelve scopes anidados.

def mostrar(tabla, nivel=0):
    print("  " * nivel, tabla.get_type(), tabla.get_name())
    for hija in tabla.get_children():
        mostrar(hija, nivel + 1)

Así puedes construir un árbol de módulos, funciones, clases, lambdas, comprehensions y otros scopes creados por el compilador.

Helpers de funciones

Las tablas de función pueden ofrecer get_parameters(), get_locals(), get_globals(), get_nonlocals() y get_frees(), según la versión.

Utiliza feature detection si la herramienta debe soportar varios Pythons.

El scope de clase es diferente

El cuerpo de una clase se ejecuta en su propio namespace, pero los métodos no capturan automáticamente los atributos de clase como variables léxicas.

class Ejemplo:
    valor = 10

    def metodo(self):
        return valor

Dentro del método, valor no significa automáticamente Ejemplo.valor. El acceso normalmente debe ser explícito.

La cell implícita __class__

El compilador puede crear una referencia especial a __class__ para funciones como super() sin argumentos.

Las herramientas deben aceptar símbolos implícitos o generados por el compilador.

Scopes de lambdas

Una lambda crea un scope de función y aparece como tabla hija.

doblar = lambda x: x * 2

El nombre de la tabla puede reflejar una etiqueta interna en lugar de un identificador elegido por el usuario.

Scopes de comprehensions

En Python moderno, las comprehensions poseen su propio scope y la variable de iteración no se filtra al bloque exterior.

cuadrados = [x * x for x in valores]

La tabla de símbolos puede mostrar un namespace hijo asociado con la comprehension.

Generator expressions

Los generator expressions también crean scopes internos y pueden capturar nombres exteriores.

Inspecciona las tablas hijas para localizar free variables y parámetros implícitos.

Funciones async

Las funciones asíncronas siguen las reglas léxicas normales, aunque su ejecución y suspensión sean diferentes.

La symbol table no determina si una coroutine fue esperada correctamente; eso requiere AST y control de flujo.

Annotations

Las type annotations pueden introducir referencias y bindings según la sintaxis y versión. La evaluación diferida y mecanismos modernos cambian cuándo se evalúan.

No infieras todo el comportamiento de runtime solo por la presencia de un símbolo relacionado con annotations.

Type aliases y nuevos tipos de tabla

Las versiones recientes introducen construcciones que pueden crear tipos adicionales de symbol table, incluidos scopes de type parameters y aliases.

Usa enums y APIs del Python en ejecución en lugar de comparar una lista fija de strings. Añade tests específicos para sintaxis nueva.

Bindings de pattern matching

El pattern matching estructural puede introducir nombres locales.

match valor:
    case {"id": identificador}:
        usar(identificador)

Symtable registra el binding, mientras AST explica que nació de un pattern.

Exception targets

El nombre en except Exception as error es un binding local con reglas especiales de limpieza después del handler.

La tabla no simula el tiempo, por lo que saber si el nombre existe después de una instrucción concreta exige control de flujo.

Eliminación con del

del nombre afecta un binding, pero la tabla no es una línea temporal de valores.

Detectar uso antes de definición o después de delete necesita un control-flow graph y análisis de datos.

Entiende UnboundLocalError

Una asignación en cualquier punto de la función puede hacer que el compilador clasifique un nombre como local, aunque una lectura aparezca antes.

x = 10

def ejemplo():
    print(x)
    x = 20

La tabla explica por qué x es local y por qué la ejecución produce UnboundLocalError.

Shadowing

Una variable local puede ocultar un import, builtin, parámetro o nombre exterior.

No todo shadowing es incorrecto. Un linter debe considerar la longitud del scope, convenciones públicas, legibilidad y necesidad del símbolo original.

Nombres built-in

Un nombre que no es local ni global explícito puede resolverse desde builtins en runtime.

La tabla no garantiza que se utilice el builtin original, porque globals y __builtins__ pueden modificarse.

Renaming más seguro

Para renombrar una variable con precisión, combina tokens para spans, AST para contexto sintáctico y symtable para clasificación del binding.

No reemplaces todas las coincidencias textuales. Atributos, claves, strings, comentarios y nombres de otros scopes son entidades distintas.

Detecta parámetros no usados

Compara parámetros con símbolos referenciados en el mismo namespace.

Ignora placeholders como _ y argumentos exigidos por protocolos, callbacks, decorators, frameworks u overrides.

Detecta imports no usados

Un import nunca referenciado puede ser una oportunidad de limpieza, pero algunos imports registran plugins o provocan efectos laterales.

Considera excepciones, reexports y __all__ antes de eliminar automáticamente.

Estado global mutable

La tabla identifica acceso global, pero no determina mutabilidad, thread safety o si una operación modifica el objeto.

Combina AST y análisis de datos para distinguir una lectura de una mutación de estado compartido.

Combina con tokenize

tokenize ofrece comentarios, grafía y posiciones. Symtable ofrece binding y scope.

Consulta tokenize en Python.

Combina con AST

AST muestra dónde aparece un nombre y qué construcción lo contiene. Symtable indica cómo lo clasifica el compilador.

Consulta ast en Python.

Combina con dis

Después del análisis de símbolos, el compilador elige instrucciones de load y store. Comparar con bytecode muestra el resultado.

Consulta dis en Python.

Análisis de proyectos

Cada archivo posee su propia tabla de módulo. Resolver imports, reexports, aliases y APIs públicas exige un índice del proyecto.

No ejecutes imports durante análisis estático: los módulos pueden producir efectos arbitrarios.

Lee el encoding correcto

Usa tokenize.open() para leer el fuente antes de llamar a symtable().

Conserva el filename real para que errores y diagnósticos apunten al lugar correcto.

Limita entradas grandes

Al analizar uploads o repositorios no confiables, limita bytes, líneas, nesting, cantidad de archivos y tiempo total.

Los servicios públicos deberían ejecutar el análisis basado en compilador dentro de un worker aislado con límites de CPU y memoria.

Diferencias entre versiones

Los tipos de tabla, helpers y clasificaciones evolucionan con nuevas construcciones del lenguaje.

Prueba cada versión soportada y usa comprobaciones de capacidades.

symtable no es un type checker

El módulo no resuelve tipos, overloads, protocolos, generics o inferencia. Describe bindings léxicos.

Integra un type checker dedicado cuando importen los tipos.

symtable no analiza control de flujo

La tabla no informa orden de ejecución, reachability o si una variable se define en todos los branches.

Construye un CFG o usa un framework de análisis para reglas temporales.

Seguridad

Una tabla limpia no vuelve seguro el código. Imports, decorators, descriptors y llamadas de runtime pueden ejecutar acciones arbitrarias.

Nunca uses una allowlist de nombres como sandbox.

Pruebas

Incluye módulos, funciones, funciones anidadas, closures, clases, lambdas, comprehensions, generators, async, global, nonlocal, annotations, pattern matching y syntax errors.

Compara resultados estáticos con ejecución únicamente en fixtures confiables.

Errores comunes

Los fallos frecuentes son inferir bindings solo por texto, confundir globals implícitos y declarados, ignorar scopes de comprehensions, tratar clases como funciones, renombrar sin namespace, usar symtable como type checker, ejecutar imports para resolver nombres y depender de una sola versión de Python.

Conclusión

symtable revela cómo el compilador organiza nombres y scopes antes de generar bytecode. Usa lookup() y las propiedades de Symbol para identificar locals, globals, imports, parámetros, nonlocals y free variables, y recorre get_children() para construir el árbol de namespaces.

Combina tablas de símbolos con tokens, AST y análisis de flujo para linting y refactoring precisos. Consulta la documentación oficial de symtable.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    dis en Python: entiende el bytecode

    Aprende dis en Python para inspeccionar bytecode, jumps, stack effects, caches adaptativos y optimizaciones sin depender de internals inestables.

    Ler mais

    Tempo de leitura: 5 minutos
    27/08/2026
    Gold Bitcoin coins displayed on a sparkling gold texture, representing digital currency and finance.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tokenize en Python: lee tokens del código

    Aprende tokenize en Python para leer tokens, comentarios, encoding, indentación y posiciones, además de transformar y reconstruir código con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Side view of contemplating female assistant in casual style standing near shelves and choosing file with documents
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zipapp en Python: crea archivos .pyz

    Aprende zipapp en Python para crear archivos .pyz, definir entry points, incluir dependencias puras, usar recursos y distribuir CLIs seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sysconfig en Python: rutas y build

    Aprende sysconfig en Python para descubrir rutas, schemes, headers, flags de build, ABI, extensiones nativas y entornos virtuales.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Código binario verde sobre el teclado de un portátil, representando datos internos de Python.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    marshal en Python: formato interno

    Aprende marshal en Python para objetos internos y bytecode, con versiones, allow_code, caches descartables, límites y riesgos de entrada no

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Vibrant assortment of pickled vegetables in jars with red fabric covers, displayed on shelves.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    copyreg en Python: personaliza pickle

    Aprende copyreg en Python para personalizar pickle, registrar reducers, versionar estado, evitar conflictos globales y serializar con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026