WedX - журнал о программировании и компьютерных науках

Как я могу иметь одну копию переменной, совместно используемую ровно двумя модулями?

Мне нужна единственная копия var, которая ведет себя глобально: т. е. ее хранилище существует один раз, и все ссылки на него в обоих модулях читают и пишут одно и то же хранилище. Как я могу получить такое поведение, используя всего два модуля?

Вот моя попытка, но она с треском провалилась:

# module1
#!python

import module2

var = 1

def touch_var():
    global var
    print(var, id(var))
    var = 3
    print(var, id(var))
    
def main():
    global var
    print(var, id(var))
    var = 2
    print(var, id(var))
    module2.do_func()
    print(var, id(var))
    touch_var()
    print(var, id(var))

if __name__ == '__main__':
    main()

# module2
#!python

import module1

def do_func():
    print(module1.var, id(module1.var))
    module1.touch_var()
    print(module1.var, id(module1.var))
    module1.var = 4
    print(module1.var, id(module1.var))

Выход:

1 17006627360
2 17006627392
1 17006627360
1 17006627360
3 17006627424
3 17006627424
4 17006627456
2 17006627392
2 17006627392
3 17006627424
3 17006627424

Я пробовал то же самое, используя назначение с использованием изменяемых типов:

#!python

import module2

var = [1]

def touch_var():
    global var
    print(var, id(var))
    var = [3]
    print(var, id(var))
    
def main():
    global var
    print(var, id(var))
    var = [2]
    print(var, id(var))
    module2.do_func()
    print(var, id(var))
    touch_var()
    print(var, id(var))

if __name__ == '__main__':
    main()

#!python

import module1

def do_func():
    print(module1.var, id(module1.var))
    module1.touch_var()
    print(module1.var, id(module1.var))
    module1.var = [4]
    print(module1.var, id(module1.var))

и я получаю этот результат:

[1] 7696579790600
[2] 7696579792072
[1] 7696579790664
[1] 7696579790664
[3] 7696579790600
[3] 7696579790600
[4] 7696579790664
[2] 7696579792072
[2] 7696579792072
[3] 7696579790600
[3] 7696579790600

И я попытался присвоить элементу изменяемого типа:

#!python

import module2

var = [1]

def touch_var():
    global var
    print(var, id(var))
    var[0] = 3
    print(var, id(var))
    
def main():
    global var
    print(var, id(var))
    var[0] = 2
    print(var, id(var))
    module2.do_func()
    print(var, id(var))
    touch_var()
    print(var, id(var))

if __name__ == '__main__':
    main()

#!python

import module1

def do_func():
    print(module1.var, id(module1.var))
    module1.touch_var()
    print(module1.var, id(module1.var))
    module1.var[0] = 4
    print(module1.var, id(module1.var))

И я получаю этот результат:

[1] 7696579790600
[2] 7696579790600
[1] 7696579790664
[1] 7696579790664
[3] 7696579790664
[3] 7696579790664
[4] 7696579790664
[2] 7696579790600
[2] 7696579790600
[3] 7696579790600
[3] 7696579790600

Все три дают одинаковый результат.

Результат, который я хочу, таков:

1
2
2
2
3
3
4
4
4
3
3

Почему я получаю то, что получаю? Как я могу получить то, что хочу?

Обратите внимание, что это НЕ дубликат Python: совместное использование глобальных переменных между модулями и классами в нем, как предполагал @prune, потому что в этом случае ответ заключался в том, что проблема возникла из-за использования from module import *, чего я здесь не делаю; и альтернативный ответ заключался в том, что проблема возникла из-за использования неизменяемых типов, что, как я показал, не является проблемой. Кроме того, одним из моих критериев является решение этого вопроса с использованием двух файлов, но в этом вопросе используются три.


Ответы:


1

Ваша проблема связана с тем, что у вас циклический импорт. Вы запускаете код в модуле 1 для запуска. Это создает одну копию пространства имен module1. Затем вы вызываете код в модуле 2 из модуля 1, где модуль 2 также импортирует модуль 1. Вызов import module1 в модуле 2 создает вторую копию пространства имен модуля 1.

Вот почему ваш первый вызов в модуле 2 выводит 1, потому что это значение изначально установлено в module1.py, и вы только что импортировали его. Все вызовы в модуле 2, даже те, которые вызывают модуль 1, работают с этой второй копией модуля 1.

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

Вот способ убедить себя, что это происходит. Сделайте копию module1.py с именем module3.py и включите ее вместо module1.py в module2.py. Вы получите точно такой же результат, за исключением того, что теперь будет ясно, что происходит, поскольку вы не ожидаете, что var в модуле1 и в модуле3 будут одной и той же переменной.

10.10.2020

2

Вот простое решение:

#!python
# module2

import __main__ as module1

Причина, по которой ваши примеры не работают должным образом, заключается в том, что исполняемый скрипт всегда добавляется в sys.modules как __main__. Поэтому, когда вы позже импортируете его используя его имя файла, вы получите ссылку на другой объект модуля. В связи с этим к нему относятся несколько по-особому, поскольку повторный импорт любого другого модуля всегда будет ссылаться на один и тот же объект в sys,modules.

(Примечание: хотя приведенное выше исправление решает вашу проблему, оно обычно считается плохой практикой).

10.10.2020
Новые материалы

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

Учебные заметки: создание моего первого пакета Node.js
Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


Для любых предложений по сайту: [email protected]