Перезагрузить субмодуль, импортированный в другие модули

У меня есть следующая проблема, и я поделюсь четырьмя разными файлами .py, чтобы лучше объяснить себя. Я запускаю код из spyder (не jupyter), python 3.4. У меня есть мастер-скрипт "master001.py", из которого я выполняю код. это выглядит так:

import sys
before = [str(m) for m in sys.modules]

from importlib import reload
import time
#from child001 import calculation as calc
import child001 
from child002 import calculation_two
from child003 import calculation_three

after = [str(m) for m in sys.modules]
print("########################")   
print([m for m in after if not m in before])
print("########################\n")




stop = False
while stop == False:
    print("\n\n\n\n\n\n\n")
    reload_child_one = input("reload child 1 function? Enter Y or N\n")
    reload_child_one = reload_child_one.lower()

    if reload_child_one == "y":
        print("Script will try to reload the calculation 1 / child 1 module.")
        time.sleep(1)
        reload(child001)



    reload_child_two = input("reload child 2 function? Enter Y or N\n")
    reload_child_two = reload_child_two.lower()

    if reload_child_two == "y":
        print("Script will try to reload the calculation 2 / child 2 module.")
        time.sleep(1)
        #reload(sys.modules[calculation_two.__module__])
        #del calculation_two
        #from child002 import calculation_two
        #__import__("child002", fromlist='calculation_two')
        calculation_two = reload(sys.modules["child002"]).calculation_two



    print("\n####################################################")
    a = input("Enter number that will be saved in variable 'a' or enter Q to quit prorgam\n")

    if a.lower() == "q" :
        stop = True
        print("\nFunction complted. Script will quit.")
        print("####################################################\n")
        time.sleep(2)

    else:
        try:
            a = int(a)

            print("Master - Launching Child function 'calculation'")
            b = child001.calculation(a)

            print("\nMaster - Inside Master file. Result = b = {}".format(b))
            print("####################################################\n")

            print("Master - Launching Child 2 function 'calculation_two' on input variable")
            c = calculation_two(a)     

            print("\nMaster - Inside Master file. Result = c = {}".format(c))            
            print("####################################################\n")

            print("Master - Launching child 3")
            calculation_three()
            time.sleep(2)

        except:
            print("input value was not a valid number. Please, try again.\n")
            print("####################################################\n")
            time.sleep(2)

master001.py вызывает child001.py для выполнения простых вычислений:

print("wassupp from child 1 !!!")

def calculation(a):

    print("\n----------------------------------------")
    print("Child 1 - function 'calculation' started.")
    print("Child 1 - Operation that will be executed is: input variable + 20")

    result = a + 20

    print("Child 1 - Returning result =  {}".format(result))
    print("----------------------------------------\n")
    return result

Затем master001.py вызывает child002.py, в котором выполняется еще одно простое вычисление:

print("wassupp from child 2 !!!")

def calculation_two(a):

    print("\n----------------------------------------")
    print("Child 2 - function  'calculation_two' started.")
    print("Child 2 - Operation that will be executed is: input variable + 200")

    result = a + 200

    print("Child 2 - Returning result =  {}".format(result))
    print("----------------------------------------\n")
    return result

Все идет нормально. Наконец, у меня есть child003.py. в этом модуле я выполняю расчет, который фактически импортируется из child002.py.

from child002 import calculation_two

print("wassupp from child 3 !!!")

def calculation_three():

    print("\n----------------------------------------")
    print("Child 3 function - Calculation will use the one in child 2 applied to value '3'.!\n")

    result = calculation_two(3)

    print("Child 3 - result =  {}".format(result))
    print("----------------------------------------\n")
    return

как вы можете видеть из запуска master001.py, когда я перезагружаю calculate_two, используя

calculation_two = reload(sys.modules["child002"]).calculation_two

это работает для запуска calculation_two из child002.py, однако не перезагружает calculation_two, вызванное child003.py.

В частности, если вы запустите master001.py и перед вводом чего-либо вручную измените содержимое calculation_two, то, когда вас спросят

reload child 1 function? Enter Y or N

вы вводите N, и когда вас спросят

reload child 2 function? Enter Y or N

вы вводите Y, вы увидите значение, возвращаемое child003.py, не отражающее новый обновленный код.

Я прочитал Как выгрузить (перезагрузить) модуль Python? и Как перезагрузить модуль python, импортированный с помощью `из модуля import *` они очень полезны, но я не могу найти там решение этой конкретной проблемы.


person Angelo    schedule 09.10.2018    source источник


Ответы (1)


Ваша проблема в том, как вы импортировали функцию из child002:

from child002 import calculation_two

Это создает ссылку на объект функции в child003, и эта ссылка не заменяется. Имена Python похожи на метки на строках, привязанные к объектам. Вы можете привязать к объекту несколько меток, и если вы хотите заменить этот объект другим, вам необходимо повторно привязать все эти метки.

Вы начинаете с этого:

sys.modules['child002']
    -> module object created from child002.py
        -> module.__dict__ (the module globals)
            -> module.__dict__['calculation_two']
                    |
                    |
                    +--> function object named calculation_two
                    |
                    |
            -> module.__dict__['calculation_two']
        -> module.__dict__ (the module globals)
    -> module object for child003.py
sys.modules['child003']

и когда вы затем перезагружаете модуль child002, Python заменяет все существующие глобальные объекты новыми объектами, так что теперь у вас есть:

sys.modules['child002']
    -> module object created from child002.py
        -> module.__dict__ (the module globals)
            -> module.__dict__['calculation_two']
                    |
                    |
                    +--> *new* function object named calculation_two


                    +--> *old* function object named calculation_two
                    |
                    |
            -> module.__dict__['calculation_two']
        -> module.__dict__ (the module globals)
    -> module object for child003.py
sys.modules['child003']

потому что ссылка calculation_two в объекте модуля child003 является независимой меткой.

Вы либо должны заменить эту метку вручную:

calculation_two = reload(sys.modules["child002"]).calculation_two
child003.calculation_two = calculation_two

или вы можете просто не ссылаться на calculation_two напрямую, а вместо этого ссылаться только на модуль child002:

import child002

# ...

def calculation_three():
    # ...
    result = child002.calculation_two(3)

в этот момент у вас есть следующие отношения:

sys.modules['child002']
    -> module object created from child002.py
       ^ -> module.__dict__ (the module globals)
       |    -> module.__dict__['calculation_two']
       |            |
       |            |
       |            +--> function object named calculation_two
       |
       |
       +------------+
                    |
                    |
            -> module.__dict__['child002']
        -> module.__dict__ (the module globals)
    -> module object for child003.py
sys.modules['child003']

Я могу порекомендовать прочитать объяснение Неда Бэтчелдера об именах и значениях Python для другого взгляда на это.

person Martijn Pieters    schedule 09.10.2018
comment
Большое спасибо, что нашли время, чтобы написать ваше полезное объяснение. Спасибо также за то, что поделились этой ссылкой, я прочитал ее дважды, очень и очень полезно. Пожалуйста, у меня последний вопрос. Если у меня есть несколько дочерних сценариев, скажем, child005.py, child009.py, child020.py и т. д., все из которых используют from child002 import calculate_two, есть ли способ проверить, сколько дочерних сценариев мне нужно обновить? (Похоже на то, что вы упомянули child003.calculation_two = calculate_two, но предполагая, что я не знаю, сколько дочерних элементов ссылаются на calculate_two) Спасибо. - person Angelo; 10.10.2018
comment
@Angelo: вам нужно будет проверить все свои модули на наличие функций и проверить их атрибут __module__. Если вы хотите регулярно перезагружать модули, гораздо лучше импортировать в другое место только объекты модуля, а не имена из этих модулей. - person Martijn Pieters; 10.10.2018
comment
спасибо Мартейн. Могу я спросить, что вы имеете в виду, когда говорите импортировать объекты модуля в другое место, пожалуйста? Вы имеете в виду, что, используя мой код выше в качестве примера, внутри child003.py я должен использовать import child вместо from child002 import calculate_two? Еще раз спасибо за вашу помощь, очень ценю. - person Angelo; 10.10.2018
comment
@Angelo: ты бы использовал import child002, да. - person Martijn Pieters; 10.10.2018