symtable no Python: analise escopos

Publicado em: 27/08/2026
Tempo de leitura: 9 minutos
A close-up of a coin-operated telescope set against a beautiful cloudy sky, ideal for travel imagery.

O módulo symtable expõe as tabelas de símbolos produzidas pelo compilador do Python antes da geração de bytecode. Ele informa quais nomes pertencem a cada escopo, quais são parâmetros, imports, variáveis locais, globals, nonlocals, free variables, referências e namespaces internos. Essa camada é útil em linters, refactorings, ferramentas educacionais, análise estática e inspeção de closures.

Uma symbol table não executa o programa e não resolve todos os comportamentos dinâmicos. Atribuições via globals(), setattr(), imports dinâmicos, decorators, metaclasses e monkey patching continuam fora de uma análise completa. Ainda assim, ela representa as decisões reais de escopo tomadas pelo compilador e é mais confiável que inferir bindings apenas a partir de tokens.

Crie uma tabela de símbolos

A função symtable.symtable() recebe o fonte, um filename e o modo de compilação.

import symtable

codigo = """
x = 10

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

tabela = symtable.symtable(codigo, "exemplo.py", "exec")
print(tabela.get_type())

O modo pode ser exec, eval ou single, como em compile().

Erros de sintaxe

A construção da tabela usa o compilador e pode gerar SyntaxError.

try:
    tabela = symtable.symtable(codigo, caminho, "exec")
except SyntaxError as erro:
    relatar(caminho, erro.lineno, erro.offset, erro.msg)

Em análise de vários arquivos, registre o erro e continue com os demais.

SymbolTable principal

A tabela raiz representa o módulo ou expressão.

print(tabela.get_name())
print(tabela.get_type())
print(tabela.get_lineno())

O nome e a linha ajudam a criar diagnósticos, mas o significado exato depende do tipo de tabela.

Identificadores

get_identifiers() devolve nomes conhecidos naquele escopo.

for nome in sorted(tabela.get_identifiers()):
    print(nome)

A coleção inclui bindings e referências relevantes ao compilador, não apenas atribuições textuais.

lookup

lookup(name) devolve um objeto Symbol com propriedades do nome.

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

Consultar um nome inexistente pode gerar erro; verifique os identificadores ou trate a exceção.

Variáveis locais

is_local() indica que o nome é local ao escopo analisado.

funcao = tabela.lookup("somar").get_namespace()
for nome in funcao.get_identifiers():
    simbolo = funcao.lookup(nome)
    if simbolo.is_local():
        print("local", nome)

Parâmetros também são locals, mas possuem a marca específica is_parameter().

Parâmetros

is_parameter() identifica argumentos de função.

for nome in funcao.get_parameters():
    print(nome)

Ferramentas podem usar essa informação para detectar argumentos não utilizados ou shadowing.

Globals implícitos e explícitos

Um nome pode ser considerado global porque não existe binding local ou porque foi declarado com global.

contador = 0

def incrementar():
    global contador
    contador += 1

Use is_global() e, quando disponível, is_declared_global() para diferenciar casos relevantes.

Nonlocal

nonlocal liga um nome a um escopo de função externo, mas não ao módulo.

def externa():
    total = 0

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

    return interna

O símbolo interno pode ser identificado por is_nonlocal().

Free variables

Uma free variable é usada por uma função interna e fornecida por um escopo externo.

interna = funcao_externa.get_children()[0]
print(interna.get_frees())

Esses nomes participam da criação de closures.

Cell variables

Quando uma variável local de uma função é capturada por uma função interna, o compilador precisa armazená-la em uma cell.

A API da symbol table permite inferir essa relação comparando locals do escopo externo com frees dos filhos. Code objects também expõem co_cellvars e co_freevars.

Imports

is_imported() informa que um nome foi introduzido por import.

import json as serializador
from pathlib import Path

O alias, e não necessariamente o nome original do módulo, aparece como binding.

Atribuições

is_assigned() identifica nomes que recebem um binding naquele escopo.

Atribuição inclui formas além de =, como targets de loops, imports, definições e determinados patterns. Confirme com AST quando precisar classificar a origem.

Referências

is_referenced() indica uso do nome em uma expressão ou operação relevante.

Uma variável atribuída mas nunca referenciada pode ser candidata a aviso, mas considere APIs públicas, efeitos de decorators e convenções de nomes.

Namespaces

Um símbolo de função ou classe pode possuir uma tabela filha.

simbolo = tabela.lookup("somar")
if simbolo.is_namespace():
    namespace = simbolo.get_namespace()

get_namespaces() é necessário quando o mesmo nome produz mais de um namespace em situações específicas.

get_children

get_children() devolve tabelas aninhadas.

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

Isso permite visualizar a árvore de escopos.

Funções

Tabelas de função oferecem helpers como get_parameters(), get_locals(), get_globals(), get_nonlocals() e get_frees(), conforme a versão.

Use feature detection quando a ferramenta suporta várias versões do Python.

Classes

Classes possuem regras de escopo diferentes de funções. O corpo é executado em um namespace próprio, mas métodos não capturam automaticamente variáveis da classe como closure.

class Exemplo:
    valor = 10

    def metodo(self):
        return valor

Nesse caso, valor dentro do método não significa automaticamente Exemplo.valor.

__class__ e métodos

O compilador pode criar uma referência especial a __class__ para recursos como super() sem argumentos.

Ferramentas devem aceitar símbolos gerados ou implícitos pelo compilador.

Lambdas

Uma lambda cria um novo escopo de função e aparece como tabela filha.

dobrar = lambda x: x * 2

O nome da tabela pode refletir a forma interna usada pelo compilador.

Comprehensions

Comprehensions possuem escopos próprios em Python moderno. A variável de iteração não vaza para o escopo externo.

quadrados = [x * x for x in valores]

A symbol table pode expor uma tabela interna associada à comprehension.

Generators

Generator expressions também criam escopos internos e podem capturar nomes externos.

Use a árvore de tabelas para localizar frees e parâmetros implícitos.

Async functions

Funções async seguem regras de escopo semelhantes às funções normais, com comportamento de execução diferente.

A tabela de símbolos não informa se uma chamada foi aguardada corretamente; isso exige AST e análise de fluxo.

Type annotations

Annotations podem criar referências e bindings conforme a sintaxe e a versão. Avaliação adiada ou mecanismos modernos de annotations afetam o comportamento.

Não conclua o runtime de uma annotation apenas pela presença de um nome na tabela.

Type aliases e novos escopos

Versões recentes da linguagem introduzem construções que podem criar tipos adicionais de symbol table. Ferramentas devem usar os enums e APIs da versão em execução em vez de comparar strings fixas.

Inclua casos de type parameters e aliases na suíte quando suportados.

Pattern matching

Patterns podem introduzir bindings locais.

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

A symbol table reconhece o binding, enquanto a AST mostra sua origem no pattern.

Exception targets

O nome em except Exception as erro recebe um binding local com regras de limpeza específicas.

Uma análise de “definido depois” precisa de controle de fluxo, não apenas da tabela.

del

del nome afeta o binding, mas a symbol table não é uma simulação temporal de valores.

Para detectar uso antes de definição ou após delete, construa um grafo de controle.

UnboundLocalError

Uma atribuição em qualquer ponto da função pode fazer o compilador classificar o nome como local, mesmo quando a leitura ocorre antes.

x = 10

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

A symbol table ajuda a explicar por que x é local e a execução gera UnboundLocalError.

Shadowing

Uma variável local pode ocultar um import, builtin ou nome externo.

Nem todo shadowing é erro. Linters devem considerar convenções, duração do escopo e clareza.

Builtins

Um nome que não é local ou global explícito pode ser resolvido em builtins em runtime.

A symbol table não garante que o builtin original será usado, porque __builtins__ e globals podem ser alterados.

Renomeação segura

Para renomear uma variável, combine tokens para posições, AST para contexto e symtable para binding.

Não substitua toda ocorrência textual do nome. Atributos, chaves de dicionário, strings e símbolos de outro escopo são diferentes.

Detecte parâmetros não usados

Compare parâmetros com símbolos referenciados no mesmo escopo.

Ignore nomes convencionais como _ e parâmetros exigidos por interfaces, callbacks ou overrides.

Imports não usados

Um símbolo importado e não referenciado pode indicar cleanup, mas imports também executam side effects ou registram plugins.

Uma ferramenta deve permitir exceções e compreender __all__.

Globals mutáveis

A tabela mostra acesso global, mas não informa thread safety ou mutabilidade.

Use análise adicional para detectar atribuições e operações sobre objetos compartilhados.

Integração com tokenize

tokenize fornece comentários, grafia e posições. Symtable fornece binding e escopo.

Veja tokenize no Python.

Integração com AST

A AST mostra onde o nome aparece e qual construção o contém. A symbol table informa como o compilador o classifica.

Veja ast no Python.

Integração com dis

Depois da tabela de símbolos, o compilador seleciona instruções de load e store apropriadas. Comparar com dis mostra o resultado.

Veja dis no Python.

Análise entre arquivos

Cada arquivo possui uma tabela de módulo independente. Resolver imports e reexports exige um índice do projeto.

Não execute imports durante análise estática; isso pode causar side effects.

Arquivos com encoding

Leia fonte com tokenize.open() antes de chamar symtable().

Preserve filename real para mensagens de erro.

Entrada grande

Limite bytes, linhas, profundidade e tempo ao analisar uploads ou repositórios não confiáveis.

Execute o compilador em um processo isolado quando o serviço for público.

Versões do Python

Tipos de tabela, métodos e classificação podem evoluir com novas construções da linguagem.

Teste a ferramenta em cada versão suportada e use feature detection.

Não é type checker

Symtable não resolve tipos, overloads, protocolos ou inferência. Ele descreve bindings léxicos.

Para análise de tipos, integre um type checker dedicado.

Não é controle de fluxo

A tabela não informa ordem de execução, branches alcançáveis ou valores definidos em todos os caminhos.

Construa CFG ou use ferramentas existentes para regras temporais.

Segurança

Analisar uma symbol table não torna seguro executar o código. Decorators, imports e runtime podem executar operações arbitrárias.

Não use uma allowlist de símbolos como sandbox.

Testes

Inclua módulos, funções, nested functions, closures, classes, lambdas, comprehensions, generators, async, global, nonlocal, annotations, match e syntax errors.

Compare resultados com execução apenas em fixtures confiáveis.

Erros comuns

Os erros mais frequentes são inferir binding apenas por texto, confundir global implícito e declarado, ignorar scopes de comprehensions, tratar classe como função, fazer rename sem namespace, usar symtable como type checker, executar imports para resolver nomes e depender de APIs de uma única versão.

Conclusão

symtable revela como o compilador organiza nomes e escopos antes do bytecode. Use lookup() e propriedades de Symbol para identificar locals, globals, imports, parâmetros, nonlocals e free variables, e percorra get_children() para construir a árvore de namespaces.

Combine a tabela com tokens, AST e análise de fluxo quando precisar de refactoring ou linting preciso. Consulte a documentação oficial de symtable.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dis no Python: entenda o bytecode

    Aprenda dis no Python para desmontar bytecode, analisar instruções, jumps, pilha, caches adaptativos e otimizações sem depender de internals instáveis.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    Flat lay of gold bitcoin coins on a pink surface, symbolizing cryptocurrency and modern investment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tokenize no Python: leia tokens do código

    Aprenda tokenize no Python para ler tokens, comentários, encoding, indentação e posições, além de transformar e reconstruir código com segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Color-coded office binders organized neatly in a storage shelf, featuring labels and a striking red binder.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipapp no Python: crie arquivos .pyz

    Aprenda zipapp no Python para criar arquivos .pyz, definir entry points, incluir dependências, usar recursos e distribuir CLIs com segurança.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sysconfig no Python: caminhos e build

    Aprenda sysconfig no Python para descobrir paths, schemes, headers, flags de build, ABI, extensões nativas e detalhes de ambientes virtuais.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    marshal no Python: formato interno

    Aprenda marshal no Python para objetos internos e bytecode, entenda versões, allow_code, limites, caches e riscos de dados não confiáveis.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    A close-up view of fresh, green cucumbers ready for pickling and preservation in Estonia.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    copyreg no Python: personalize o pickle

    Aprenda copyreg no Python para personalizar pickle, registrar redutores, versionar estado, evitar conflitos globais e serializar com segurança.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026