Рассматриваемая проблема лучше всего иллюстрируется этим минимальным примером, содержащим 3 сценария:

< Сильный > foo.py

global_val = [0]

< Сильный > bar.py

from foo import global_val

def work(val=global_val[0])
    print("global_val: ", global_val[0])
    print("val: ", val)

< Сильный > main.py

from bar import work
import foo

if __name__ == '__main__':
    foo.global_val[0] = 1
    work()

Что, я ожидаю, будет на выходе:

global_val: 1
val: 1

Фактический результат:

global_val: 1
val: 0

Я не понимаю, почему аргумент по умолчанию для val в bar.py не 1. Я запутался, потому что я явно обновляю global_val перед вызовом work(), но по какой-то причине старое значение все еще используется в качестве аргумента функции по умолчанию.

Кажется, что аргумент по умолчанию каким-то образом предварительно вычисляется, когда global_val импортируется в bar.py. Разве код Python не должен динамически компилироваться во время выполнения?

Я использую Python 3.6, если это поможет.

2
Ali250 5 Июл 2019 в 19:40

5 ответов

Лучший ответ

Ключ заключается в том, что def work(val=global_val[0]): оценивается во время импорта (например, когда from bar import work попадает в main.py.) Аргументы по умолчанию для функции в Python оцениваются в тот момент, когда функция определен и хранится в его {{X3} } (вот как вы можете их проверить).

Таким образом, порядок операций:

  1. Run main.py
  2. from bar import work
  3. Найдите и загрузите bar
  4. from foo import global_val
  5. Найдите и загрузите foo
  6. def work(val=global_val[0]):
  7. Создайте функцию с именем work и оцените ее параметры по умолчанию (global_val[0] == 0)
  8. foo.global_val[0] = 1
  9. Вызвать work.
3
Sean Vieira 5 Июл 2019 в 17:51

Я думаю, когда компилятор видит это:

def work(val=global_val[0])

Значение по умолчанию val будет таким, каким было global_val [0] в то время. Установка этого значения позже не изменит определение функции, т. Е. Установите переменную val в 0 (которая является первым элементом global_val), если аргумент для этого параметра не указан.

Попробуйте что-то вроде этого:

def work(val=None):
  if not val:
    global global_val
    val = global_val[0]

Здесь вы устанавливаете val для чего-то известного, что вы можете поймать, None в этом случае, а затем устанавливаете правильное значение. Использование ключевого слова global гарантирует, что вы используете переменную из глобального пространства имен. Это установит правильное значение global_val, если оно будет изменено позже после компиляции определения функции.

0
Lurvas777 5 Июл 2019 в 17:19
item = 0

def bar(val=item):
    print(val)

bar(2)  # 2
bar()  # 0
item = 1
bar()  # 0

Это связано с аргументами по умолчанию, а не с глобальными. Аргументы по умолчанию оцениваются один раз , а не каждый раз, когда вызывается функция.

0
Michael Bianconi 5 Июл 2019 в 16:47

Поскольку значения по умолчанию установлены во время определения / импорта, вам лучше всего установить флаг или None в качестве значения по умолчанию и проверить этот флаг в начале функции, где вы можете затем назначить объект из доступной области (конечно), во время выполнения в качестве фактического значения по умолчанию вы хотите.

0
recursively 5 Июл 2019 в 17:08

Если я не ошибаюсь, то, что происходит в тот момент, когда вы импортируете рабочий func из bar.py, аргумент по умолчанию выполняется, и не имеет значения, если вы измените значение позже, потому что аргумент по умолчанию уже был «объявлено» при импорте, поскольку аргументы по умолчанию оцениваются только один раз

1
Davichete 5 Июл 2019 в 16:47