Python внутренний метод не может получить переменную из внешнего метода - PullRequest
0 голосов
/ 29 февраля 2020

Код ниже:

def cycle(f1, f2, f3):
    """Returns a function that is itself a higher-order function.

    >>> def add1(x):
    ...     return x + 1
    >>> def times2(x):
    ...     return x * 2
    >>> def add3(x):
    ...     return x + 3
    >>> my_cycle = cycle(add1, times2, add3)
    >>> identity = my_cycle(0)
    >>> identity(5)
    5
    >>> add_one_then_double = my_cycle(2)
    >>> add_one_then_double(1)
    4
    >>> do_all_functions = my_cycle(3)
    >>> do_all_functions(2)
    9
    >>> do_more_than_a_cycle = my_cycle(4)
    >>> do_more_than_a_cycle(2)
    10
    >>> do_two_cycles = my_cycle(6)
    >>> do_two_cycles(1)
    19
    """
    "*** YOUR CODE HERE ***"
    def execution(n):
        def inner(x):
            result = x
            while (n > 0):
                n = n - 1
                if (n >= 0):
                    result = f1(result)
                n = n - 1
                if (n >= 0):
                    result = f2(result)
                n = n - 1
                if (n >= 0):
                    result = f3(result)
            return result
        return inner
    return execution

запустить тестирование в терминале

$ python -m doctest xx.py

получить ошибку:

UnboundLocalError: local variable 'n' referenced before assignment

Я думаю, что переменная n используется в методе inner можно получить из внешнего метода execution, так как во время выполнения необходимо сначала вызвать execution, чтобы вызвать метод inner, поэтому переменная n должна быть инициализирована. Какую ошибку я сделал здесь? Это python 3.8.1, который я использовал.

1 Ответ

2 голосов
/ 01 марта 2020

Что случилось и почему? Основываясь на области видимости переменной, вы ожидаете, что переменная будет найдена во внешней области (если она не определена), и она действительно работает, попробуйте это:

def o(n):
    def i():
        print("now in inner", n)
    print("passed to outer", n)
    i()

o(42)

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

def o(n):
    def i():
        n = "in"
        print("now in inner", n)
    print("passed to outer", n)
    i()
    print("back in outer", n)

o(42)

Так почему Вы видели UnboundLocalError исключение? Поскольку python будет рассматривать любую локальную переменную для своей области, если она назначена в этой области, если на нее ссылаются до такого назначения, вместо этого к ней не будет обращаться во внешней области, но считается, что она еще не назначена локально:

def o(n):
    def i():
        print("now in inner", n)
        n = "in"
    print("passed to outer", n)
    i()
    print("back in outer", n)

o(42)

Если вы добавите оператор nonlocal, как предложено в комментариях, ошибка исчезнет, ​​поскольку переменная обращается к объему, а не считается локальной, однако это также означает, что любая изменения / (повторные) назначения также влияют на внешнюю область видимости, и это (особенно в более крупной программе) может вызывать удивление и сбивать с толку:

def o(n):
    def i():
        nonlocal n
        print("now in inner", n)
        n = "in"
    print("passed to outer", n)
    i()
    print("back in outer", n)

o(42)

Чтобы сблизить связанные биты и явные отношения, это может было бы лучше передать значение переменной во внутреннюю функцию, выполнить любой лог c, который он должен выполнить, и заставить его вернуть результат:

def o(n):
    def i(inner_n):
        print("now in inner", n)
        inner_n += 1
        return inner_n
    print("passed to outer", n)
    print("i() returned", i(n))
    print("back in outer", n)

o(42)

Чтобы указать на документацию :

Следующие конструкции связывают имена: ... цели, которые являются идентификаторами, если встречаются в назначении t ...

Если имя связано в блоке, оно является локальной переменной этого блока, если не объявлено как nonlocal или global.

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

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