Python: изменить локальную переменную метода внутри внутреннего метода

Я пишу тестовый метод под названием, скажем, test_foo (используя pytest). Я проверяю поведение функции, foo, который принимает в качестве аргумента другую функцию, get, foo звонки get итеративно, условно на возвращаемое значение getНапример:

def foo(get, param):
    max_num_tries = 3
    curr_num_tries = 0
    response = get(param)
    while curr_num_tries < max_num_tries and response.status_code == BAD_STATUS_CODE:
        response = get(param)
    return response

Я пытаюсь переопределить get так что он имеет доступ к тому, сколько раз он был вызван и может соответственно возвращать разные значения.

Вот упрощенная версия того, что я имею до сих пор:

def test_foo():
    tries_so_far = 0

    def get(arg1):
        global tries_so_far
        if tries_so_far < 3:
            tries_so_far += 1
            print("do something special here")
        else:
            print("do something else here")
        return "some return val"

    foo(get, "some arg")

Однако я получаю следующую ошибку:

NameError: global name 'tries_so_far' is not defined

Если я определю tries_so_far вне test_fooНа уровне модуля я получаю ожидаемое поведение. Тем не менее, я хотел бы tries_so_far быть переменной, которая является локальной для test_foo,

Есть ли способ дать get читать / писать в tries_so_far используя глобалы или какую-то другую технику? Примечание: я не могу изменить параметры или вернуть значение get,

1 ответ

Решение

В соответствии с принятым ответом на этот вопрос Почему функции в Python могут печатать переменные в закрытой области видимости, но не могут использовать их в присваивании? В Python 3 добавлен дополнительный оператор: nonlocal это делает то, что вы хотите. Это как global, но говорит, что нужно смотреть на объем, а не на уровень модуля. Поэтому следующая модификация должна позволять вам делать именно то, что вы хотите:

def test_foo():
    tries_so_far = 0

    def get(arg1):
        nonlocal tries_so_far
        if tries_so_far < 3:
            tries_so_far += 1
            print("do something special here")
        else:
            print("do something else here")
        return "some return val"

    foo(get, "some arg")

Хотя ваш вопрос не является точной копией приведенного выше, вам следует прочитать принятый ответ. Это очень хорошо и, вероятно, ответит на многие неустановленные вопросы, которые у вас есть по этому вопросу.

Другие вопросы по тегам