首页 > 编程问答 >导入创建对对象的意外引用,具体取决于模块是否是文件,或者是否是包的一部分

导入创建对对象的意外引用,具体取决于模块是否是文件,或者是否是包的一部分

时间:2024-07-21 04:15:26浏览次数:15  
标签:python import python-packaging

我有一个微妙的导入问题,它创建的变量引用的对象与我期望的不同,这完全取决于我正在编写的模块是“按原样”导入还是作为包的一部分导入。

假设我有一个定义 Config 类和设置它的函数的简单模块:

class Config:
    def __init__(self, **kwargs):
        if kwargs.get("print_keys", None):
            self.print_keys = kwargs.pop("print_keys")
        else:
            self.print_keys = ["centre", "shortName"]


rcParams = Config()


def set_config(config=None):
    global rcParams
    print("Before change:", id(rcParams))
    if config is None:
        config = Config()
    rcParams = config
    print("After change:", id(rcParams))

如果我将其保存在名为 simple.py 的模块中,那么我可以使用此模块成功测试它:

# import gribtool as gt # note this line is uncommented later
import simple as gt     # note this line is commented later


def test_set_config():
    config = gt.Config(print_keys=["centre"])
    print("Initial rcParams id:", id(gt.rcParams))
    gt.set_config(config=config)
    print("Updated rcParams id:", id(gt.rcParams))
    assert gt.rcParams.print_keys == ["centre"]


test_set_config()

输出为:

Initial rcParams id: 128205347716160
Before change: 128205347716160
After change: 128205347716832
Updated rcParams id: 128205347716832

到目前为止一切顺利。现在的问题是我希望它成为一个包的一部分。我已将此模块复制(实际上是链接)到名为 gribtool 的包(目录)中,然后创建具有预期内容的 __init__.py 文件:

from .simple import *

目录内容如下:


├── gribtool
│   ├── __init__.py
│   └── simple.py -> ../simple.py
├── simply.py
└── test.py

然后,我在上面的测试模块中取消注释了带有 import gribtool as gt 的行,然后它失败并显示以下输出:

Initial rcParams id: 134251413862864
Before change: 134251413862864
After change: 134251413864064
Updated rcParams id: 134251413862864
Traceback (most recent call last):
  File "/home/navarro/AEMET/GRIB_TOOL/test_config.py", line 13, in <module>
    test_set_config()
  File "/home/navarro/AEMET/GRIB_TOOL/test_config.py", line 10, in test_set_config
    assert gt.rcParams.print_keys == ["centre"]
AssertionError

如您所见,问题是 rcParams set_config 函数中被修改的 simple 模块中的不同 global 尽管有

指令。


遇到的问题与 Python 导入系统及其处理模块级全局变量的方式有关。让我们分解一下发生的事情:

场景 1:直接导入模块

当像这样导入 simple 模块时:

import simple as gt

实际上是创建了模块 simple 的一个副本,并将其命名为 gt 。因此, gt.rcParams simple.rcParams 都指向内存中的同一个对象。当调用 gt.set_config() 时,它会修改 gt.rcParams ,这反过来也修改了 simple.rcParams ,因为它们是同一个对象。

场景 2:从包导入

但是,当将 simple.py 放在一个包中并像这样导入时:

import gribtool as gt

情况就不同了。现在, gt 指向的是包 gribtool ,而 gt.rcParams 实际上是 gribtool.rcParams 。当 __init__.py 文件执行 from .simple import * 时,它确实将 simple.rcParams 的值复制到了 gribtool.rcParams 中。 但是 ,这仅仅是一个值的复制,而不是对原始对象的引用。

因此,当调用 gt.set_config() 时,它修改的是 gribtool.rcParams ,而不是 simple.rcParams 。这解释了为什么看到 id(gt.rcParams) 在调用 set_config() 后没有改变。

解决方案

为了解决这个问题,需要确保 set_config 函数修改的是 simple 模块中的 rcParams 对象,即使它是从包中导入的。可以通过在 set_config 函数中显式地导入 simple 模块来实现这一点:

def set_config(config=None):
    import simple  # Explicitly import the 'simple' module
    print("Before change:", id(simple.rcParams))
    if config is None:
        config = Config()
    simple.rcParams = config
    print("After change:", id(simple.rcParams))

通过这样做,无论是直接导入 simple 模块还是从包中导入, set_config 函数都会修改同一个 rcParams 对象。

其他注意事项

  • 尽量避免使用模块级全局变量,尤其是在处理可变对象(如列表或字典)时。这可能会导致难以调试的问题。
  • 考虑使用类来封装的配置,并使用类方法来修改配置。这将提供更好的封装性和可维护性。

希望这可以帮助理解问题的原因并提供解决方案!

标签:python,import,python-packaging
From: 78773672

相关文章

  • 当值来自函数 python unittest 时,如何模拟全局变量
    我必须在python中模拟全局变量,但变量值来自另一个函数。当我导入文件时,这个函数正在运行,但我想要那里的模拟值。secrets.pyimporttracebackimportloggingimportboto3importosimportjsonlogger=logging.getLogger()logger.setLevel(logging.INFO)secret_......
  • 使用 python print 和 gdb 时出现 BrokenPipeError
    我正在尝试在Linux中运行应用程序并使用Python生成输入:python3-c'print(".....")'|./someapp但出现下一个错误:Exceptionignoredin:<_io.TextIOWrappername='<stdout>'mode='w'encoding='utf-8'>BrokenPipeError:......
  • python 舰队容器
    我正在尝试使用容器在flet中制作一个菜单,它应该是半透明的,但其中的项目不是。我尝试将opacity=1分配给元素,但没有成功-它们与容器一样透明感谢任何帮助我的代码:nickname=ft.TextField(label="xxx",hint_text="xxx")column=ft.Column(controls=[nickname......
  • Python应用程序跨子包共享的配置文件
    我正在构建一个应用程序来控制一些硬件。我在包中实现了不同类型的硬件:电机和测量设备。我的文件结构如下:name_of_my_app/__init__.pymain.pyconfig.iniCONFIG.pymotors/__init__.pyone_kind_of_motor.pymeasurement_devices/......
  • python中时间序列数据的梯度计算
    我正在尝试编写一个函数,它可以从最适合下面的线返回梯度dataframe在浏览了谷歌的几个资源之后,我仍然不确定这是如何完成的。我明白最佳拟合线的计算公式为:y=mx+b将因变量(y)设置为foos,将自变量(x)设置为DateTimeDatafram......
  • 调试用 C 编写的 Python 扩展
    我非常熟悉编写C代码,并且很擅长编写Python代码。我正在尝试学习如何用C编写可以从OSX10.15.7上的Python-3.9.X调用的模块。我已经得到了几个“helloworld”类型的示例,但是对于复杂的示例,我正在努力弄清楚如何调试我编写的C扩展。MWE:src/add.c//......
  • 具有块大小选项的 Python pandas read_sas 因索引不匹配而失败并出现值错误
    我有一个非常大的SAS文件,无法容纳在我的服务器内存中。我只需要转换为镶木地板格式的文件。为此,我使用pandas中chunksize方法的read_sas选项分块读取它。它主要是在工作/做它的工作。除此之外,一段时间后它会失败并出现以下错误。此特定SAS文件有794......
  • 使用 requests 包 python 时打开文件太多
    我正在使用Pythonrequests包向API发出大量请求。然而,在某些时候,我的程序由于“打开的文件太多”而崩溃。当我明确关闭我的会话时,我真的不知道这是怎么回事。我使用以下代码:importrequestsimportmultiprocessingimportnumpyasnps=requests.session()s.keep......
  • Python 是一种选择性解释语言吗?为什么下面的代码不起作用?
    由于程序是从上到下运行的,为什么下面的代码不执行块中的第一行就直接抛出错误?if5>2:print("TwoislessthanFive!")print("Fiveisgreaterthantwo!")错误:文件“/Users/____/Desktop/Pythonpractise/practise.py”,第3行print("五比二大!")Indentati......
  • 裁剪时间变量 Python Matplotlib Xarray
    我不确定这是否是一个愚蠢的问题,但我想按时间变量剪辑.nc文件。我在xarray中打开了数据集,但以下ds.sel行(之前已运行)仅返回错误。ds=xr.open_dataset('/Users/mia/Desktop/RMP/data/tracking/mcs_tracks_2015_11.nc')selected_days=ds.sel(time=slice('2015-11-22',......