Рекурсивная функция Python, загруженная из модуля - PullRequest
0 голосов
/ 26 ноября 2018

Если у нас есть рекурсивная функция func1 в модуле mod1, и мы импортируем ее, используя оба оператора:

import mod1

и

from mod1 import func1

В основном мы будем иметь две ссылки на один объект:

id(func1) == id(mod1.func1)

Тогда, если мы обновим код func1 внутри файла mod1.py, а затем перезагрузим mod1, но не саму функцию (func1):

imp.reload(mod1)

у нас будет два разных объекта:

id(func1) != id(mod1.func1)

Однако, если мы вызываем func1 (* args) - сначала он вызывает func1, но все последующие рекурсивные вызовы он вызывает mod1.func1

Это желаемое поведение в python?

Вот примеры кода: mod1.py:

def func1(n):
  print("n is: {}".format(n))
  if n==0: return 1
  return n*func1(n-1)

Затем изменено:

def func1(n):
  # print("n is: {}".format(n))
  if n==0: return 1
  return n*func1(n-1)

Вот вывод REPL:

>>> from mod1 import func1
>>> func1(2)
n is: 2
n is: 1
n is: 0
2
>>> import mod1
>>> mod1.func1(2)
n is: 2
n is: 1
n is: 0
2
>>> id(func1)
4304551720
>>> id(mod1.func1)
4304551720
>>> ## ** Here mod1 code is updated: ** ##
>>> import imp
__main__:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
>>> imp.reload(mod1)
<module 'mod1' from '/home/user/workspace/python/tests/mod1.py'>
>>> id(mod1.func1)
4305274128
>>> id(func1)
4304551720
>>> mod1.func1(2)
2
>>> func1(2)
n is: 2
2
>>> 

Это кажется немного запутанным.Это желаемое поведение в Python?

1 Ответ

0 голосов
/ 27 ноября 2018

Трудно сказать, является ли поведение желательным (для кого), но оно ожидается.

Нет большой разницы между вашим образцом кода и следующим:

In [3]: class Foo:
   ...:     def qwe(self, once_again=True):
   ...:         print('original qwe')
   ...:         if once_again:
   ...:             self.qwe(once_again=False)
   ...:     qwe1 = qwe
   ...:
   ...:     def qwe(self, once_again=True):
   ...:         print('new qwe')
   ...:         if once_again:
   ...:             self.qwe(once_again=False)
   ...: a = Foo()
   ...: a.qwe1()
   ...:
   ...:
original qwe
new qwe

После выполнения оператора import mod1 и func1 являются просто переменными, которые хранят ссылки на соответствующие объекты.Когда вы перезагружаете модуль, вы просто назначаете другое значение переменной с именем mod1.

Кстати, вы можете наблюдать аналогичный эффект, когда сохраняете ссылку на регистратор уровня модуля или уровня впеременная: LOG = logging.getLogger(__name__), так как ничто не мешает пользователю вашего кода вызывать logging.config.dictConfig где-то в середине срока службы приложения.Если новая конфигурация предназначена для подавления вывода из вашего модуля, ваш код не будет ничего знать о новом логгере для вашего модуля, и вы продолжите запись в журнал.

Я бы сказал, что поведениепо крайней мере предназначен.

UPD: вопрос оказался немного сложнее.

Если переменная отсутствует в текущей лексической области действия функции, она берется из области действия модуля.(если это не замыкание, в этом случае используется поле __closure__).Чтобы получить область действия модуля, функция обращается к переменным уровня модуля через sys.modules[func1.__module__], который был обновлен с помощью reload.

...