Refactorización para eliminar variable global en función recursiva.

En resumen

Estoy creando una estructura de árbol a partir de una entrada de archivo de texto usando una función de la pregunta SO: Análisis de archivos Python: Crear árbol a partir de un archivo de texto . Pero solo puedo producir mi árbol utilizando una variable global y no puedo encontrar una manera de evitar esto.

Datos de entrada

En un archivo llamado data.txt tengo lo siguiente:

 Root -A 10 -B --BA 2 --BB 5 --BZ 9 --BX ---X 4 ---Y ----Y0 67 ----Y1 32 ---Z 3 -C 19 

Resultado deseado

 {'B': ['BA 2', 'BB 5', 'BZ 9', 'B X'], 'B X': ['X 4', 'Y', 'Z 3'], 'Root': ['A 10', 'B', 'C 19'], 'Y': ['Y0 67', 'Y1 32']} 

Mi código

 import re, pprint PATTERN = re.compile('^[-]+') tree = {} def _recurse_tree(parent, depth, source): last_line = source.readline().rstrip() while last_line: if last_line.startswith('-'): tabs = len( re.match(PATTERN, last_line).group() ) else: tabs = 0 if tabs = depth: if parent is not None: print "%s: %s" %(parent, node) if parent in tree: tree[parent].append(node) else: tree[parent] = [ node, ] last_line = _recurse_tree(node, tabs+1, source) return last_line def main(): inFile = open("data.txt") _recurse_tree(None, 0, inFile) pprint.pprint(tree) if __name__ == "__main__": main() 

El problema

¿Cómo me deshago del tree variables globales? Todo lo que hago parece hacer que el código sea mucho más largo o más feo, pero me gustaría usar la función en gran medida y odio depender de los efectos secundarios para el resultado principal.

Suplemento

Después de las respuestas a continuación, revisé el código para devolver el tree de la siguiente manera. ¿Es esto un python? Devolver una tupla y luego lanzar el primer elemento parece poco elegante.

 def _recurse_tree(parent, depth, source, tree=None): if tree is None: tree = {} last_line = source.readline().rstrip() while last_line: if last_line.startswith('-'): tabs = len( re.match(PATTERN, last_line).group() ) else: tabs = 0 if tabs = depth: if parent is not None: print "%s: %s" %(parent, node) if parent in tree: tree[parent].append(node) else: tree[parent] = [ node, ] last_line, tree = _recurse_tree(node, tabs+1, source, tree) return last_line, tree def main(): inFile = open("data.txt") tmp, tree = _recurse_tree(None, 0, inFile) pprint.pprint(tree) 

Tu variable de tree ya es mutable; solo pásalo junto con tus llamadas recursivas:

 def _recurse_tree(parent, depth, source, tree=None): if tree is None: tree = {} last_line = source.readline().rstrip() while last_line: if last_line.startswith('-'): tabs = len( re.match(PATTERN, last_line).group() ) else: tabs = 0 if tabs < depth: break node = re.sub(PATTERN, '', last_line.strip()) if tabs >= depth: if parent is not None: print "%s: %s" %(parent, node) if parent in tree: tree[parent].append(node) else: tree[parent] = [ node, ] last_line = _recurse_tree(node, tabs+1, source, tree) return last_line 

Alternativamente, puedes usar una clase para mantener el estado, entonces será más fácil extraer el estado de la instancia:

 class TreeBuilder(object): _PATTERN = re.compile('^[-]+') def __init__(self, source): self.tree = {} self.source = source self._recurse_tree() def _recurse_tree(self, parent=None, depth=0): last_line = self.source.readline().rstrip() while last_line: if last_line.startswith('-'): tabs = len( self._PATTERN.match(last_line).group() ) else: tabs = 0 if tabs < depth: break node = self._PATTERN.sub('', last_line.strip()) if tabs >= depth: if parent is not None: print "%s: %s" %(parent, node) if parent in self.tree: self.tree[parent].append(node) else: self.tree[parent] = [ node, ] last_line = self._recurse_tree(node, tabs+1) return last_line 

Entonces usa esto así:

 def main(): inFile = open("data.txt") builder = TreeBuilder(inFile) pprint.pprint(builder.tree) 

Creo que es una buena solución aquí: crear una clase y poner un árbol dentro, como un miembro de una clase privada.

O simplemente puede pasar este diccionario como uno de los parámetros en función y pasarlo durante la recursión. Pasará por referencia, por lo que todas las funciones de tiempo usarán el mismo diccionario sin variable global. Pero prefiero la clase.

Utilice el tree como parámetro predeterminado en su función:

 def _recurse_tree(parent, depth, source, tree = None): if tree is None: # needed on first invocation tree = {} 

Llámelo por primera vez sin parámetro de tree , y en cada llamada sucesiva, agregue otro tree parámetros a él.

Entonces, desde dentro de tu método, tu llamada recursiva se convierte en:

 last_line = _recurse_tree(node, tabs+1, source, tree)