如何避免Python中的“重复模块导入陷阱”?

问题描述 投票:0回答:1

我最近遇到了一个关于 Python 中模块导入的令人困惑的问题,特别是根据不同的导入样式复制类实例的方式。

我必须努力才能繁殖。这并不容易。

主要问题

根据导入风格,模块变量可以重复(实例化多次,即使没有循环导入)。在编程时很难看到这一点,而且调试起来也很乏味。

主要问题

避免此问题的最佳做法是什么?

插图

这是一个简单的项目

PROJECT ROOT FOLDER
└───app
    │   main.py
    │
    └───websocket
            a.py
            b.py
            ws.py
            __init__.py

main.py

import sys


def log_modules():
    print("\n\nModules currently in cache:")
    for module_name in sys.modules:
        if ("web" in module_name or "ws" in module_name) and ("windows" not in module_name and "asyncio" not in module_name):
            print(f" - {module_name}: {id(sys.modules[module_name])}")


from app.websocket.a import a
log_modules()
from app.websocket.b import b
log_modules()


if __name__ == "__main__":
    a()
    b()

ws.py


class ConnectionManager:
    def __init__(self):
        print(f"New ConnectionManager object created, id: {id(self)}")
        self.caller_list = []

    def use(self, caller):
        self.caller_list.append(caller)
        print(f"ConnectionManager object used by {caller}, id: {id(self)}. Callers = {self.caller_list}")


websocket_manager = ConnectionManager()

a.py

from websocket.ws import websocket_manager  # <= one import style: legitimate


def a():
    websocket_manager.use("a")

b.py

from .ws import websocket_manager  # <= another import style: legitimate also


def b():
    websocket_manager.use("b")

它输出:

New ConnectionManager object created, id: 1553357629648
New ConnectionManager object created, id: 1553357630608
ConnectionManager object used by a, id: 1553357629648. Callers = ['a']
ConnectionManager object used by b, id: 1553357630608. Callers = ['b']

当我们期望只有一个

ConnectionManager
实例时。

我相信这两种导入都是合法的,特别是在可能出现不同风格的开发团队中(即使我们不希望:出现此问题)。

问题是:盲目应用的最佳做法应该是什么

补充问题 模块日志显示:

Modules currently in cache:
 - app.websocket: 1553355041312
 - websocket: 1553357652672
 - websocket.ws: 1553357652512    <= here we are after main.py from app.websocket.a import a
 - app.websocket.a: 1553355040112
 - app.websocket.ws: 1553357653632
 - app.websocket.b: 1553357652112  <= here we are after main.py from app.websocket.b import b

我们可以看到问题:

ws
模块被导入两次:一次作为
websocket.ws
,另一个作为
app.websocket.ws

有人可以用简单的方式解释一下吗?我不能;-)
我觉得我们正处于 python 的一个复杂的方面,通常我们不想打扰它! Python 在很多方面都很简单...

感谢您的帮助。

python python-import python-module
1个回答
0
投票

正确做法:

  • __init__.py
    添加到
    app/
    ,使
    app
    成为一个包(而不是作为隐式命名空间包)。
  • PROJECT ROOT FOLDER
    ,使用
    app/main.py
    python -m app.main
    作为模块运行。 (不要运行
    python app/main.py
    或类似内容。)

这样就不可能

app.websocket
导入为
websocket
,因为它根本无法在
sys.path
上使用。 (只有
PROJECT ROOT FOLDER
会位于
sys.path
上,并且
websocket
不是它的直接后代,因此它不会像
import websocket
一样导入。)

© www.soinside.com 2019 - 2024. All rights reserved.