Почему при поиске BST появляется ошибка «локальная переменная определена в области видимости»? - PullRequest
1 голос
/ 26 апреля 2020

Я попытался ответить на следующий вопрос ( ссылка leetcode ):

Учитывая узел root двоичного дерева поиска, вернуть сумму значений всех узлов с значение между L и R (включительно). Бинарное дерево поиска гарантированно будет иметь уникальные значения.

Это официальное решение:

class Solution:

    def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int:

        def dfs(node):

            if node:
                if L <= node.val <= R:
                    self.total += node.val
                if L <= node.val:
                    dfs(node.left)
                if node.val <= R:
                    dfs(node.right)

        self.total = 0
        dfs(root)
        return self.total

solution = Solution()
print(solution.rangeSumBST(root, L, R))

Работает, как и ожидалось, например, для следующего ввода: root = [10,5,15,3,7,null,18], L = 7, R = 15 , вывод 32. Но почему возникает ошибка, если я пишу ее как простую функцию без оператора класса?

def rangeSumBST(root: TreeNode, L: int, R: int) -> int:

    def dfs(node):

        if node:
            if L <= node.val <= R:
                total += node.val
            if L <= node.val:
                dfs(node.left)
            if node.val <= R:
                dfs(node.right)

    total = 0
    dfs(root)
    return total

print(rangeSumBST(root, L, R))

Это сообщение об ошибке: Local variable 'total' defined in enclosing scope on line 75 referenced before assignment... (pyflakes E) Второй фрагмент кода выглядит практически идентично, но я могу Не скажу, почему объектно-ориентированный стиль решения работает, пока я получаю сообщение об ошибке, записав его в виде простой функции. Очевидно, что с переменной 'total' что-то не так, но не должна ли переменная, определенная вне области функции, быть также доступной внутри области функции?

Ответы [ 2 ]

2 голосов
/ 26 апреля 2020

Вот минимальный пример, воспроизводящий вашу проблему:

def enclosing():
    def inner():
        total += 1

    total = 0
    inner()
    return total

enclosing()

Это вызывает ошибку:

----> 3         total += 1
      4 
      5     total = 0

UnboundLocalError: local variable 'total' referenced before assignment

Это совершенно нормально: Python считает, что переменная является локальной, если Вы назначаете ему в любом месте функции. Таким образом, total считается локальным в inner, и вы пытаетесь увеличить его, хотя вы не указали его значение, поэтому возникает ошибка. То же самое произошло бы с функцией верхнего уровня и глобальной переменной total.

У вас нет такой же проблемы с методом, поскольку self не имеет ничего назначенного ему, вы просто измените значение его атрибута total.

Решение здесь состоит в том, чтобы указать Python искать total во включающей области видимости, вот для чего было введено ключевое слово nonlocal в Python 3 (см. PEP 3104 ):

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

Имена, перечисленные в нелокальном выражении, в отличие от перечисленных в глобальном выражении, должны ссылаться на уже существующие привязки в охватывающая область (область, в которой должна быть создана новая привязка, не может быть определена однозначно).

def enclosing():
    def inner():
        nonlocal total
        total += 1

    total = 0
    inner()
    return total

enclosing()
# 1
1 голос
/ 26 апреля 2020

Поскольку функция должна зависеть только от своих аргументов и, возможно, некоторых глобальных переменных, но не локальных переменных в своем контексте.

total определяется в контексте rangeSumBST функции, которая содержит контекст, в котором вы вызываете функцию dfs. Но это нехорошо.

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

Примерно так должно работать:

def rangeSumBST(root: TreeNode, L: int, R: int) -> int:

    def dfs(node, total):
        if node:
            if L <= node.val <= R:
                return total + node.val
            if L <= node.val:
                return dfs(node.left, total)
            if node.val <= R:
                return dfs(node.right, total)

    return dfs(root, 0)
...