Python:嵌套函数的外部作用域中的引用变量(非全局)

时间:2016-09-12 08:04:34

标签: python python-3.x tree-traversal

我试图递归一棵树并跟踪遍历的路径,直到我找到一个我正在寻找的元素的位置。但是,我遇到两个问题:

  1. 虽然我当前的代码返回正确的解决方案,但它有点hacky。我必须将当前遍历的路径推送到final_path,然后返回final_path[0]。如果我只是尝试设置final_path = path,其中final_path在外部范围内定义,则它不起作用。如何引用嵌套函数的外部范围?

  2. 如果树中的值不是,我最终会以预先订购的方式遍历整个树。有没有办法构造代码,以便我可以说"如果,在遍历结束时,我们还没有找到目标元素,那么只需返回[]而不是完整路径" 。我意识到我可以循环检查每个元素,但这似乎非常多余。

  3. 代码:

    lftlft = {'val': 3, 'left': None, 'right': {'val': 100, 'left': None, 'right': None}}
    rtrt = {'val': 5, 'left': None, 'right': None}
    lft = {'val': 2, 'left': lftlft, 'right': {'val': 99, 'left': None, 'right': None}}
    rt = {'val': 4, 'left': None, 'right': rtrt}
    T = {'val': 1,'left': lft, 'right': rt}
    
    def get_path(root, data, path):
        final_path = []
    
        def pre_order(tree, path):
            if tree is None:
                return
    
            path.append(tree['val'])
    
            if tree['val'] == data:
                final_path.append(path)
                return True
    
            return pre_order(tree['left'], path[:]) or pre_order(tree['right'], path[:])
    
        pre_order(root, [])
        print('finalpath', final_path)
        return final_path[0]
    
    get_path(T, 99, [])
    

4 个答案:

答案 0 :(得分:6)

在Python 3.x中,您只需使用关键字nonlocal

您可以在使用global的地方使用它:在内部函数的开头:

def get_path(root, data, path):
    final_path = ...

    def pre_order(tree, path):
        nonlocal final_path
        ...
    ...

    return final_path

即使在Python 2.x中,只需引用变量就会自动授予您读取对变量的访问权限 - 但绝不会写入访问权限。请注意,如果引用的对象是可变对象,如列表或字典,则可以在内部函数内进一步修改它。

通过在Python 3.0中引入nonlocal关键字,您可以对外部函数范围中定义的变量具有完全写入权限。 ]

将内部函数写入外部作用域列表中的固定元素的hack可能是在Python 2.x中解决它的最佳方法。

答案 1 :(得分:0)

我意识到我不必迭代检查它是否被发现。不知道为什么我没想到早点添加旗帜。

rtrt = {'val': 5, 'left': None, 'right': None}
lft = {'val': 2, 'left': lftlft, 'right': {'val': 99, 'left': None, 'right': None}}
rt = {'val': 4, 'left': None, 'right': rtrt}
T = {'val': 1,'left': lft, 'right': rt}


def get_path(root, data, path):
    final_path = []
    found = False

    def pre_order(tree, path):
        if tree is None:
            return

        path.append(tree['val'])

        if tree['val'] == data:
            nonlocal final_path, found
            final_path = path
            found = True
            return found

        return pre_order(tree['left'], path[:]) or pre_order(tree['right'], path[:])

    pre_order(root, [])
    if not found:
        return []

    return final_path

get_path(T, 999, [])

答案 2 :(得分:0)

我认为这是因为当您从pre-order致电get_path时,您无法获取返回值。

以下内容似乎与我的代码示例相同,如果我将值99更改为其他数字,则返回None

lftlft = {'val': 3, 'left': None, 'right': {'val': 100, 'left': None, 'right': None}}
rtrt = {'val': 5, 'left': None, 'right': None}
lft = {'val': 2, 'left': lftlft, 'right': {'val': 99, 'left': None, 'right': None}}
rt = {'val': 4, 'left': None, 'right': rtrt}
T = {'val': 1,'left': lft, 'right': rt}

def get_path(root, data, path):

    def pre_order(tree, path):
        if tree is None:
            return

        path.append(tree['val'])

        if tree['val'] == data:
            return path
        return pre_order(tree['left'], path[:]) or pre_order(tree['right'], path[:])

    desired_result = pre_order(root, [])
    return desired_result

print (get_path(T, 99, []))

答案 3 :(得分:0)

这是XY problem。你可以做你要求的,但它实质上是在你的功能中添加全局状态。好吧,使用final_path已经意味着你正在使用全局状态,你应该尽可能避免(在这种情况下可能)。

有几种方法可以解决您的问题。首先,您可以扩展final_path而不是追加。也就是说,将final_path.append(path)替换为final_path.extend(path)。不是将path作为元素添加到final_path,而是将path的所有元素添加为final_path的元素。但你仍然在使用全球状态,这并不是很好。

相反,您可以使用"真实性" Python对象,以便您可以返回不同的类型。也就是说,您可以返回路径本身,而不是返回True,如果返回路径,则只追加当前val。如果您没有找到路径,则返回类似虚假的值,例如[]。这将创建一个反向路径,但您最后可以反转path。例如

def get_path(root, data, path):

    def pre_order(tree):
        if not tree:
            # base case, data not found
            return [] # counts as false
        elif tree['val'] == data:
            # base case, found data
            return [data] # counts as true
        else:
            # recursive case
            path = pre_order(tree['left']) or pre_order(tree['right'])
            if path:
                # data was found, add the current val to the path
                path.append(tree['val'])
            return path

    final_path = pre_order(root)
    final_path.reverse()
    print('finalpath', final_path)
    return final_path

最后,您要如何编写解决方案的答案是使用关键字nonlocal。这允许您更改final_path指向的对象,而不仅仅是改变对象。例如

def f():
    x = 1
    def g():
        nonlocal x
        x = 2
    g()
    print("x =", x) # prints x = 2