【问题标题】:Python 3 NOT updating variables in a nested package (”recursive” relative imports used)Python 3 不更新嵌套包中的变量(使用“递归”相对导入)
【发布时间】:2015-05-30 22:00:02
【问题描述】:

我正在重构一个大型过程程序(在一个文件夹中实现多个文件)并使用包将文件分组为面向对象的结构。此应用程序使用 tKinter(可能是红鲱鱼),并且正在使用 Eclipse Kepler 上的 PyDev 开发(在 Win7 上)。

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

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

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

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

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

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

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

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

顺便说一句,更高级别模块中使用的更新行是“varC = X.getTable()”;这会从自定义类返回 tKinter IntVars 的矩阵(列表列表)。

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

有没有办法让我的蛋糕也吃掉?是否有一种“非类”方式让“varC”驻留在第 4 级并且仍然可以由更高级别的包更新?还是使用共同祖先是唯一可行的“非类”方法?

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

添加于 2015 年 3 月 27 日:

这是实际文件的两个 Eclipse 屏幕截图。 (对于那些不熟悉 Eclipse 的人,__init__.py 显示为带有包名称的选项卡。)第一张照片显示了递归导入(从下到上)。第二个显示功能,“SiteSpecificReports”的“未使用导入”(黄色三角形)警告。以蓝色突出显示的行是“未使用变量”警告所在的位置(它神秘地消失了)。

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

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

2105 年 10 月 23 日更新

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

接受的答案(包括链接)提供了我需要的线索。我的问题是,当我将一个文件/模块重构为几个子文件(每个子文件都包含变量定义和 修改 它们的函数)时,我认为每个子文件都是一个类似类的“黑盒子” object 而不是更准确地说是一个简单的“将此文件插入到更高级别的文件”命令(如编辑器“粘贴”命令)。

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

在创建子文件时,似乎要使变量对其他子文件可见,您需要在 topmost 文件而不是 bottommost 文件中定义它 - 这会破坏我试图使用的“object-ish”方法。有点遗憾的是,这种方法不起作用,因为变量的位置使得在其他模块中重用子文件变得很尴尬。将每个文件转换为一个类应该可以完成我所追求的 - 但是当您只需要“在此处插入此代码块”效果时,这似乎真的毫无意义。

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

【问题讨论】:

  • 您想提供您的目录结构和隐含文件(或代码模式下的 Ascii 艺术)的图像,以及您声明常量、函数 var 等的方式吗?

标签: python python-3.x python-import


【解决方案1】:

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

from .blah import Foo

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

Foo = 1

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

这个blog post 是一个很好的澄清。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-18
    • 2021-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-19
    • 1970-01-01
    • 2013-06-03
    相关资源
    最近更新 更多