Изображение гика

Блог питониста

Немного об импортах в питоне

12 ноября 2017 г.

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

Допустим есть такой проект:

project
|   main.py
|   test1.py
|   test2.py
|   config.py

main.py:

 1 import config as conf
 2 import test1
 3 import test2
 4 
 5 print('    in main')
 6 print(conf.test_var)
 7 test1.test1()
 8 print('    again in main')
 9 print(conf.test_var)
10 test2.test2()

test1.py:

1 import config as conf
2 
3 
4 def test1():
5     print('    in test1, executing test1()')
6     conf.test_var = 'test1'

test2.py:

1 import config as conf
2 
3 
4 def test2():
5     print('    in test2, executing test2()')
6     print(conf.test_var)

config.py:

1 test_var = 'initial_value'

Что будет если выполнить main.py? Сначала будет выведено начальное значение переменной. Затем будет вызвана функция test1, которая поменяет ее значение, будет выведено новое значение, но что будет потом в функции test2? Будет ли загружена заново переменная test_var? python main.py выведет следующее:

    in main
initial_value
    in test1, executing test1()
    again in main
test1
    in test2, executing test2()
test1

Можно было подумать (так было со мной), что последняя строчка будет другой - что там тоже должна быть выведена строка "initial_value", поскольку в модуле test2 config вроде бы заново импортируется, но python кэширует импорты, поэтому значение переменной не обновляется, а остается такой же как и в функции test1.

test1.py:

1 import config as conf
2 
3 
4 def test1():
5     print('    in test1, executing test1()')
6     print(id(conf.test_var))
7     conf.test_var = 'test1'
8     print(id(conf.test_var))

test2.py:

1 import config as conf
2 
3 
4 def test2():
5     print('    in test2, executing test2()')
6     print(id(conf.test_var))
7     print(conf.test_var)

python main.py:

    in main
initial_value
    in test1, executing test1()
140355198581312
140355198588464
    again in main
test1
    in test2, executing test2()
140355198588464
test1

Во-первых, при присваивании нового значения переменной test_var в функции test1 создается новая переменная, во-вторых, после изменения в функции test1 та же самая переменная test_var задействована в функции test2. Впрочем, такое поведение можно изменить, если принудительно перезагрузить config, test2.py:

 1 # python2
 2 import config as conf
 3 
 4 
 5 def test2():
 6     print('    in test2, executing test2()')
 7     print(id(conf.test_var))
 8     reload(conf)
 9     print(id(conf.test_var))
10     print(conf.test_var)

Если вы используете python3, то используйте следующий код для импорта reload, потому что она была перемещена:

1 import importlib
2 importlib.reload(module)
3 ...

python main.py:

    in main
initial_value
    in test1, executing test1()
140009528056384
140009528063536
    again in main
test1
    in test2, executing test2()
140009528063536
140009528056384
initial_value

Теперь видно, что config был перезагружен в функции test2.py, и теперь в ней после перезагрузки config'a используется изначальное значение переменной test_var.

Вывод


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

Метки

python
Если вам понравился пост, можете поделиться им в соцсетях: