从模块重新加载的 Python 递归函数

Posted

技术标签:

【中文标题】从模块重新加载的 Python 递归函数【英文标题】:Python recursive function reloaded from module 【发布时间】:2019-04-28 08:43:08 【问题描述】:

如果我们在模块 mod1 中有递归函数 func1 并且我们使用两个语句导入它:

import mod1

from mod1 import func1

基本上我们会有两个指向单个对象的链接:

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

如果我们更新 mod1.py 文件中的 func1 代码,然后重新加载 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】:

很难说这种行为是否(为谁)是可取的,但它是意料之中的。

您的代码示例与以下代码没有太大区别:

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 语句执行后,mod1func1 只是保持对相应对象的引用的变量。重新加载模块时,只需为名为 mod1 的变量分配另一个值。

顺便说一句,当您将对模块级别或类级别记录器的引用存储在变量中时,您可能会观察到类似的效果:LOG = logging.getLogger(__name__),因为没有什么能阻止您的代码用户在中间的某处调用logging.config.dictConfig应用程序的生命周期。如果较新的配置旨在抑制模块的输出,则您的代码将不知道有关模块的新记录器的任何信息,并且您将继续写入日志。

我会说,这种行为至少是有意的。

UPD:问题变得有点复杂。

如果在函数的当前词法范围内缺少该变量,则从模块范围中获取该变量(如果它不是闭包,在这种情况下使用__closure__ 字段)。为了获得模块的作用域,函数通过sys.modules[func1.__module__] 访问模块级变量,reload 已更新。

【讨论】:

以上是关于从模块重新加载的 Python 递归函数的主要内容,如果未能解决你的问题,请参考以下文章

python函数装饰器内置函数json及模块

python第五天:协程,匿名函数,递归函数,模块导入,re模块

python的集合递归函数和模块

小白有一个关于python递归的问题 求教

python协程函数递归匿名函数与内置函数使用模块与包

Python模块化编程-高阶函数#学习猿地