декораторы и методы python

Новенький тут. Также я (очень) новичок в python и пытаюсь понять следующее поведение. Может ли кто-нибудь объяснить мне, почему два метода в этом примере имеют разный результат?

def map_children(method):
    def wrapper(self,*args,**kwargs):
        res = method(self,*args,**kwargs)
        for child in self._children:
            method(child,*args,**kwargs)            
        return res
    return wrapper

class Node(object):

    def __init__(self,name,parent=None):
        self._namestring = name
        if parent:
            self._parent = parent

        self._children = []

    @map_children
    def decorated(self):
        if hasattr(self,'_parent'):
            print '%s (child of %s)'%(self._namestring,self._parent._namestring)
        else:
            print '%s'% self._namestring

    def undecorated(self):
        if hasattr(self,'_parent'):
            print '%s (child of %s)'%(self._namestring,self._parent._namestring)
        else:
            print '%s'% self._namestring

        for child in self._children:
            child.undecorated()


def runme():
    parent = Node('parent')

    child1 = Node('child1',parent)
    child2 = Node('child2',parent)
    grandchild = Node('grandchild',child1)
    child1._children.append(grandchild)
    parent._children.append(child1)
    parent._children.append(child2)

    print '**********result from decorator**********'
    parent.decorated()

    print '**********result by hand**********'
    parent.undecorated()

Вот вывод в моей системе:

In[]:testcase.runme()
**********result from decorator**********
parent
child1 (child of parent)
child2 (child of parent)
**********result by hand**********
parent
child1 (child of parent)
grandchild (child of child1)
child2 (child of parent)

Так почему же декорированный вызов никогда не спускается к узлу-внуку? Я явно что-то упустил в синтаксисе...


person Community    schedule 30.03.2009    source источник


Ответы (1)


В декораторе вы перебираете дочерние элементы узла и вызываете для них исходный нерекурсивный method

method(child, *args, **kwargs)

так что вы только пойдете на один уровень вглубь. Попробуйте заменить эту строку на

map_children(method)(child, *args, **kwargs)

и вы получите тот же результат, что и ручная рекурсивная версия.

person dF.    schedule 31.03.2009
comment
Спасибо! Знал, что должно быть что-то подобное. Я попробовал это с нотацией @, но это не сработало (очевидно), и я не смог найти правильный синтаксис. Затем мне удалось убедить себя, что это трансформирует реальный метод, так что это не имеет значения. Я должен перестать думать об этом как о правильных макросах. - person ; 31.03.2009
comment
Я думаю, что этот подход не будет делать то, что вы ожидаете, если Node является подклассом, а подкласс имеет свою собственную версию метода... - person simon; 02.04.2009