Python 3 不更新嵌套包中的变量(使用“递归”相对导入)

Python 3 NOT updating variables in a nested package (”recursive” relative imports used)

我正在重构一个大型 程序 程序(在一个文件夹中实现尽可能多的文件)并使用包将文件分组到 object 导向结构中。此应用程序使用 tKinter(可能是红鲱鱼),并且正在 Eclipse Kepler(适用于 Win7)上使用 PyDev 进行开发。

它确实使用了一些 classes 但将包结构(见下文)转换为 classes 不是首选解决方案(除非它是获得我想要的东西的唯一合理方法)。

在 4 级包嵌套的底部,我定义了一个常量(“conA”)、一个函数(“funcB”)和一个变量(“varC”)。从下往上,每个级别的 __init__.py 文件(在实现包的(嵌套)文件夹内)包含:

from .levelbelowModuleName  import conA
from .levelbelowModuleName  import funcB
from .levelbelowModuleName  import varC

以便“递归”导入使“level4”实体在所有级别都可以通过其“level4”名称可见。

在更高级别的包中,对所有实体的“只读”引用不会导致 warning/error 消息。目前没问题。

但是当尝试在更高级别的包中更新“varC”时,我收到两个警告:1) 文件顶部的“未使用的导入:varC”警告,以及 2) 在更新函数内部(有一个“global varC”语句)一个“未使用的变量:varC”警告在“varC =”行。到目前为止还不错,因为两个“varC”没有关联(但请参阅下面令人讨厌的问题)。

我认为(从 Lutz 的“学习 Python”一书的第 23/24 章推断)导入的名称 - 在所有级别 - 会引用相同的 object(地址) - 因此更新驻留在“侄子”(兄弟child)包中的变量会起作用。将“varC”提升到最近的共同祖先包(问题包的直接 parent,“侄子”的 grandparent)似乎是一个有效的解决方法(它消除了警告)——但是这样做破坏了 object-oriented 包结构的目的。

转换为绝对导入没有帮助。 Renaming/aliasing“varC”(通过在导入中使用“as”)没有帮助。

顺便说一句,高层模块中使用的更新行是“varC = X.getTable()”;这个 returns 来自自定义 class.

的 tKinter IntVars 的矩阵(列表列表)

讨厌的问题:问题文件中任何地方对“varC”的“只读”引用,例如“print(varC)”无论是在文件顶部还是在函数内部,都会消除这两个警告,隐藏问题。

有没有办法既吃我的蛋糕又吃它?是否有一种“non-class”方式让“varC”驻留在 level4 并且仍然可以被更高级别的包更新?还是使用共同祖先是唯一可行的 simple-to-understand ”non-class” 方法?

P.S。在输入此问题时建议的相关问题中,none 似乎适用。一个类似但更简单(未嵌套)的问题是: How to change a module variable from another module?

2015 年 3 月 27 日添加:

这是实际文件的两个 Eclipse 屏幕截图。 (对于那些不熟悉 Eclipse 的人,__init__.py 显示为一个包含包名称的选项卡。)第一个镜头显示递归导入(从下到上)。第二个显示函数,"SiteSpecificReports" 的 "unused import"(黄色三角形)警告。以蓝色突出显示的行是 "unused variable" 警告所在的位置(它神秘地消失了)。

忽略所有与 LongitudeReports 有关的内容,它本质上是 MessageCountReports 的克隆。为清楚起见,我隐藏了与问题无关的所有代码(例如 tKinter 调用)。忽略文件名上的红色"X";所有都是 tKinter "init time" 类型不匹配,当代码为 运行 时消失(根据 MessageCountReports __init__.py 中 "SiteSpecificReports" 上面的评论)。

在模块层次结构中,问题文件以灰色突出显示。 "A_Mainline.py" 是执行点,下面的所有内容都是正在重构的代码(有些已经移到该文件上面的包中)。最后,除CZQX外,"SiteSpecific"下的所有子包都是占位符,只包含一个空的__init__.py文件。

2105-10-23 更新

所有这一切背后的要点是通过将每个模块分成几个源文件来将文件大小保持在合理的水平。

接受的答案(包含在 link 中)提供了我需要的线索。我的问题是,当我将 file/module 重构为几个子文件(每个子文件都包含变量定义和 修改 的函数)时,我认为每个子文件都是 class-like "black box" object 而不是,更准确地说,是一个简单的 "insert this file into the higher level file" 命令(就像编辑器 "paste" 命令)。

我的想法是,递归导入实际上会将较低级别变量的地址递归提升到较高级别的“init.py”命名空间,因此使这些变量对模块的所有其他子文件可见(谁只会 引用 这些变量) - 并允许我吃蛋糕(本地化定义)并吃掉它(变量可用在最高层)。这种方法在其他 编译 语言中对我有用,尤其是 ADA 83。

创建子文件时,似乎要使变量对其他子文件可见,您需要在 topmost 文件而不是 bottommost[= 中定义它75=] 一个 - 这打败了我试图使用的 "object-ish" 方法。有点可惜的是,这种方法不起作用,因为变量的位置使得在其他模块中重用子文件变得很尴尬。将每个文件转换为 class 应该可以完成我想要的 - 但当您只需要 "insert this code block here" 效果时,这似乎毫无意义。

无论如何,重要的是现在一切正常。

您在 python 中遗漏了 namesvalues 之间的区别。名称存在于名称空间中并指向值。当你说:

from .blah import Foo

您正在当前命名空间中创建一个 new 名称,它指向与 Foo 相同的 valueblah 命名空间中。如果在那之后你说:

Foo = 1

这会将您的 本地名称空间的 Foo 更改为指向 1,但它对 blah.Foo 没有任何作用——您会明确地说 blah.Foo = 1 更改居住在 blah.

中的名称 Foo

blog post 是一个很好的澄清。