Работа с глобальными переменными Python при использовании рекурсивных функций - PullRequest
3 голосов
/ 12 июля 2011

Я сделал программу, которая извлекает текст из файла HTML. Он рекурсивно просматривает HTML-документ и возвращает список тегов. Например,

input никоим образом вы не делаете это

output ['no', 'way', 'you', 'are' ...] .

Вот очень упрощенный псевдокод для этого:

def get_leaves(node):
    kids=getchildren(node)
    for i in kids:
        if leafnode(i):
            get_leaves(i)
        else:
            a=process_leaf(i)
            list_of_leaves.append(a)

def calling_fn():
    list_of_leaves=[] #which is now in global scope
    get_leaves(rootnode)
    print list_of_leaves    

Я сейчас использую list_of_leaves в глобальной области видимости из вызывающей функции. Вызывающая_fn () объявляет эту переменную, к ней добавляется get_leaves ().

Мой вопрос , как мне изменить мою функцию, чтобы я мог делать что-то вроде list_of_leaves = get_leaves (rootnode), то есть без использования глобальной переменной?

Я не хочу, чтобы каждый экземпляр функции дублировал список, поскольку список может быть довольно большим.

Пожалуйста, не критикуйте дизайн этого конкретного псевдокода, так как я упростил это. Он предназначен для другой цели: извлечение токенов вместе со связанными тегами с помощью BeautifulSoup

Ответы [ 5 ]

11 голосов
/ 12 июля 2011

Вы можете передать список результатов в качестве необязательного аргумента.

def get_leaves(node, list_of_leaves=None):
    list_of_leaves = [] if list_of_leaves is None else list_of_leaves
    kids=getchildren(node)
    for i in kids:
        if leafnode(i):
            get_leaves(i, list_of_leaves)
        else:
            a=process_leaf(i)
            list_of_leaves.append(a)

def calling_fn():
    result = [] 
    get_leaves(rootnode, list_of_leaves=result)
    print result

Объекты Python всегда передаются по ссылке. Это обсуждалось ранее здесь . Некоторые из встроенных типов являются неизменяемыми (например, int, string), поэтому вы не можете изменять их на месте (новая строка создается, когда вы объединяете две строки и присваиваете их переменной). Экземпляр изменяемых типов (например, list) может быть изменен на месте. Мы используем это, передавая исходный список для накопления результата в наших рекурсивных вызовах.

Для извлечения текста из HTML в реальном приложении лучше использовать зрелую библиотеку, такую ​​как BeautifulSoup или lxml.html (как предлагали другие).

8 голосов
/ 12 июля 2011

Нет необходимости передавать аккумулятор в функцию или обращаться к нему через глобальное имя, если вы превращаете get_leaves() в генератор:

def get_leaves(node):
    for child in getchildren(node):
        if leafnode(child):
            for each in get_leaves(child):
                yield each
        else:
            yield process_leaf(child)

def calling_fn():
    list_of_leaves = list(get_leaves(rootnode))
    print list_of_leaves
2 голосов
/ 12 июля 2011
Ответ генератора

@ pillmincher - лучший, но в качестве другой альтернативы вы можете превратить свою функцию в класс:

class TagFinder:
    def __init__(self):
        self.leaves = []

    def get_leaves(self, node):
        kids = getchildren(node)
        for i in kids:
            if leafnode(i):
                self.get_leaves(i)
            else:
                a = process_leaf(i)
                self.list_of_leaves.append(a)
def calling_fn():
    finder = TagFinder()
    finder.get_leaves(rootnode)
    print finder.list_of_leaves

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

2 голосов
/ 12 июля 2011

Используйте достойный HTML-парсер, например BeautifulSoup, вместо того, чтобы пытаться умнее существующего программного обеспечения.

0 голосов
/ 12 июля 2011

Как общий вопрос о рекурсии, это хороший вопрос.Обычно имеется рекурсивная функция, которая накапливает данные в некоторой коллекции.Либо коллекция должна быть глобальной переменной (плохой), либо она передается в рекурсивной функции.Когда коллекции передаются практически на каждом языке, передается только ссылка, поэтому вам не нужно беспокоиться о пространстве.Кто-то только что опубликовал ответ, показывающий, как это сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...