Ошибка переменной области видимости во время рекурсии в Python - PullRequest
1 голос
/ 20 ноября 2011

Следующий код рекурсивно обрабатывает список словарей в дерево при построении строки вывода HTML. Я получаю ошибку доступа к области при попытке доступа к строковой переменной output из рекурсивной функции. Однако, у него нет проблем с доступом к объекту списка nodes в той же области видимости, и на самом деле функция работала нормально до того, как я добавил output var. В чем здесь дело?

Пример: http://ideone.com/Kg8ti

nodes = [ 
{ 'id':1, 'parent_id':None, 'name':'a' },
{ 'id':2, 'parent_id':None, 'name':'b' },
{ 'id':3, 'parent_id':2, 'name':'c' },
{ 'id':4, 'parent_id':2, 'name':'d' },
{ 'id':5, 'parent_id':4, 'name':'e' },
{ 'id':6, 'parent_id':None, 'name':'f' }
]

output = ''

def build_node(node):
    output += '<li><a>'+node['name']+'</a>'
    subnodes = [subnode for subnode in nodes if subnode['parent_id'] == node['id']]
    if len(subnodes) > 0 : 
        output += '<ul>'
        [build_node(subnode) for subnode in subnodes]
        output += '</ul>'
    output += '</li>'
    return node

output += '<ul>'
node_tree = [build_node(node) for node in nodes if node['parent_id'] == None]
output += '</ul>'   

import pprint
pprint.pprint(node_tree)

Ошибка:

Traceback (most recent call last):
  File "prog.py", line 23, in <module>
    node_tree = [build_node(node) for node in nodes if node['parent_id'] == None]
  File "prog.py", line 13, in build_node
    output += '<li><a>'+node['name']+'</a>'
UnboundLocalError: local variable 'output' referenced before assignment

Ответы [ 3 ]

3 голосов
/ 20 ноября 2011

Ошибка проистекает из этого:

output = ''
def add(s):
    output = output + s

, что эквивалентно output+=s. Два output в расширенном виде относятся к разным областям. Присвоение устанавливает локальную переменную, в то время как выражение справа ссылается на глобальную (поскольку local еще не установлено).

Python обнаруживает такие конфликты и выдает ошибку.

Другие предложили использовать global, чтобы Python знал, что оба output -s относятся к глобальной области видимости, но, поскольку здесь вы объединяете строки, в Python для этой задачи используется еще лучшая идиома:

output_list = []
def add(s):
    output_list.append(s)
# ...
output = ''.join(output_list)

Здесь вы не устанавливаете переменную в функции, поэтому нет необходимости в global. Строки всех вызовов добавляются в список и, наконец, объединяются с использованием '' (пустая строка) в качестве разделителя. Это намного быстрее, чем добавление строк с использованием +=, потому что строки в Python неизменны, поэтому каждый раз, когда вы добавляете две строки, Python должен создавать новую строку. Это вводит много памяти копирования.

1 голос
/ 20 ноября 2011

Хотя вы можете использовать объявление global, было бы намного понятнее передать output в качестве параметра.

Глобальные переменные считаются плохой практикой и там, где есть хорошие методы, чтобы избежать их использования, вы должны воспользоваться этими методами.

1 голос
/ 20 ноября 2011
def build_node(node):
    global output
    # proceed as before
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...