Matt Layman

Vamos a obtener un poco de «meta» sobre la programación.

¿Cómo»sabe» el programa Python(mejor conocido como intérprete) cómo ejecutar su código?Si eres nuevo en la programación, puede parecer que magic.In de hecho, todavía me parece mágico después de ser profesional durante más de una década.

El intérprete de Python no es mágico(lamento decepcionarlo).Sigue un conjunto predecible de pasos para traducir su código a instrucciones que una máquina puede ejecutar.

A un nivel bastante alto, esto es lo que sucede con su código:

  1. El código se analiza (es decir, se divide) en una lista de piezas generalmente llamadas tokens.Estos tokens se basan en un conjunto de reglas para cosas que deben tratarse de manera diferente.Por ejemplo,la palabra clave if es un token diferente de un valor numérico como 42.
  2. La lista de tokens sin procesar se transforma para construir un Árbol de Sintaxis Abstracta, AST, que es el tema que exploraremos más en este artículo.Un AST es una colección de nodos que están vinculados entre sí basados en el grammar del lenguaje Python.No se preocupe si eso no tiene sentido ahora, ya que le daremos más luz momentáneamente.
  3. A partir de un árbol de sintaxis abstracta,el intérprete puede producir una forma de instrucción de nivel inferior llamada bytecode.Estas instrucciones son cosas como BINARY_ADD y están destinadas a ser muy genéricas para que un equipo pueda ejecutarlas.
  4. Con las instrucciones de código de bytes disponibles, el intérprete finalmente puede ejecutar su código.El bytecode se utiliza para llamar a funciones en su sistema operativo que, en última instancia, interactuarán con una CPU y memoria para ejecutar el programa.

Muchos más detalles podrían caber en esa descripción, pero ese es el esbozo de cómo se ejecutan los caracteres escritos por las CPU de la computadora.

ASTs como herramientas de análisis

En el momento en que su código fuente se convierte en código de bytes,es demasiado tarde para comprender mucho lo que escribió.Bytecode es muy primitivo y muy ajustado para hacer el intérprete fast.In en otras palabras, el código de bytes está diseñado para computadoras sobre personas.

Por otro lado, los árboles de sintaxis abstractas tienen suficiente información estructurada para que sean útiles para aprender sobre su código.Los ASTs todavía no son muy amigables con la gente,pero son más sensatos que la representación de bytecode.

Debido a que Python es un lenguaje «baterías incluidas», las herramientas que necesita para usar ASTs están integradas en la biblioteca estándar.

La herramienta principal para trabajar con ASTs es el módulo ast.Veamos un ejemplo para ver cómo funciona esto.

ast por ejemplo

A continuación se muestra el script de Python de ejemplo que usaremos.Este script responde a la pregunta de » ¿qué módulos se importaron?»

import astfrom pprint import pprint

def main():with open("ast_example.py", "r") as source:tree = ast.parse(source.read())

analyzer <span style="color:#f92672">=</span> Analyzer()analyzer<span style="color:#f92672">.</span>visit(tree)analyzer<span style="color:#f92672">.</span>report()

class Analyzer(ast.NodeVisitor):def init(self):self.stats = {«import»: , «from»: }

<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">visit_Import</span>(self, node): <span style="color:#66d9ef">for</span> alias <span style="color:#f92672">in</span> node<span style="color:#f92672">.</span>names: self<span style="color:#f92672">.</span>stats<span style="color:#f92672">.</span>append(alias<span style="color:#f92672">.</span>name) self<span style="color:#f92672">.</span>generic_visit(node)<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">visit_ImportFrom</span>(self, node): <span style="color:#66d9ef">for</span> alias <span style="color:#f92672">in</span> node<span style="color:#f92672">.</span>names: self<span style="color:#f92672">.</span>stats<span style="color:#f92672">.</span>append(alias<span style="color:#f92672">.</span>name) self<span style="color:#f92672">.</span>generic_visit(node)<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">report</span>(self): pprint(self<span style="color:#f92672">.</span>stats)

if name == «main»:main()

Este código hace un par de cosas importantes:

  1. Transforma el texto de un archivo Python(en este caso, el código de ejemplo en sí)en un árbol de sintaxis abstracta.
  2. Analiza el AST para extraer información de él.

Puede ejecutar este código como:

$ python3 ast_example.py{'from': , 'import': }

Transformar a AST

with open("ast_example.py", "r") as source: tree = ast.parse(source.read())

En dos líneas de código,podemos leer un archivo y crear un AST nombre tree.La función ast.parse hace que esto sea muy sencillo!Hay un montón de cosas sucediendo bajo el capó de esa función que podemos ignorar felizmente.

Con una llamada de función,Python procesó todos los tokens, siguió todas las reglas del lenguaje y construyó una estructura de datos (p. ej., un árbol) que contiene toda la información relevante para ejecutar el código.

Antes de continuar,tomemos un momento para considerar qué es un árbol.Los árboles son un desarrollo de software de topicina muy profundo, por lo tanto, considere esto como una explicación más que exhaustiva.

Un árbol es una forma de contener datos como un conjunto de «nodos» conectados por «bordes».»

 +-----+ | A | +-----+ / \ / \+-----+ +-----+| B | | C |+-----+ +-----+

En este diagrama,a, B, y C son todos nodesand hay bordes de conexión de a a B y de a a C.

Una forma de representar este árbol en el código podría ser:

class Node: def __init__(self, value): self.value = value self.children = tree = Node('A')tree.children.append(Node('B'))tree.children.append(Node('C'))

Observe que el tree es en realidad un nodo!Cuando trabajamos con un árbol, realmente estamos tratando con una colección de nodos,y la variable de árbol es una referencia al nodo «raíz» (por ejemplo, nodo A).Al tener este tipo de estructura,podemos verificar cada nodo en el árbol y tomar medidas.Lo hacemos visitando cada nodo en el árbol y procesando sus datos.

def print_node_value(value): print(value)def visit(node, handle_node): handle_node(node.value) for child in node.children: visit(child, handle_node)# tree is from the previous example.visit(tree, print_node_value)# This should print:# A# B# C

Ahora que tenemos una idea de lo que es un árbol,podemos considerar lo que hace la siguiente sección del script de ejemplo.La estructura de árbol del árbol de sintaxis abstracta de Python es más implicadapor el recuento de sus nodos y el tipo de datos almacenados,sin embargo, la idea central de nodos y bordes es la misma.

Analizar el AST

Una vez que tenemos el árbol,el Analyzer sigue el patrón de visitantes que mostré anteriormente para extraer información del árbol.

Noté que un AST de Python es más complejo que mi diseño básico Node.Una diferencia es que rastrea varios tipos de nodos.Aquí es donde ast.NodeVisitor es útil.

A NodeVisitor puede responder a cualquier tipo de nodo en Python AST.To visite un tipo particular de nodo,debemos implementar un método que se parezca a visit_<node type>.

Mi código de ejemplo está tratando de averiguar sobre imports.To para obtener más información sobre las importaciones,el código extrae de los tipos de nodo Import y ImportFrom.

def visit_Import(self, node): for alias in node.names: self.stats.append(alias.name) self.generic_visit(node)def visit_ImportFrom(self, node): for alias in node.names: self.stats.append(alias.name) self.generic_visit(node)

Este código toma el nombre del módulo y lo almacena en una lista de estadísticas.Si bien el código no es elegante,muestra cómo interactuar con nodos AST.

Con la clase NodeVisitor definida,podemos usarla para analizar el árbol.

analyzer = Analyzer()analyzer.visit(tree)

El visit método de delegado para su visit_<node type> methodwhenever que tipo de nodo es encounteredwhile atravesar la estructura de árbol.

Entonces, ¿qué tipos de nodos hay?Puede encontrar la lista completa en la sección Gramática abstracta de la documentación del módulo ast.A decir verdad, encuentro esa documentación un poco difícil de asimilar.Puede tener más éxito al referirse a una guía más exhaustiva como la guía de nodos de Serpientes de Árbol Verde.

Terminando

A estas alturas,esperamos que entiendas cómo:

  1. Construir un AST a partir del código fuente de Python.
  2. Hacer análisis en el AST utilizando un NodeVisitor.

Creo que puedes responder muchas preguntas interesantes sobre tu código usando árboles de sintaxis abstractas.Preguntas como:

  • ¿Cuántas variables utilicé?
  • ¿Cuáles son las llamadas a funciones más comunes en mi código?
  • ¿Mis módulos están estrechamente acoplados entre sí?
  • ¿Qué bibliotecas de terceros aparecen con frecuencia en diferentes paquetes?

El módulo ast probablemente no sea una herramienta a la que llegarás muy often.In esas veces que necesitas ast, su API mínima es bastante memorable y puedes analizar el código rápidamente.

Si esto te resulta útil, ¿te importaría compartirlo en Twitter o en tu sitio de redes sociales favorito?Me gusta chatear con la gente sobre este tipo de temas, para sentirme libre de twittearme a@mblayman.

¡Aprende sobre Python!

Puede unirse a mi boletín junto con más de 1,000 desarrolladores para ayudarlo a aprender más sobre Django y Python.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.