封装导入模块的范围

Encapsulate the scope of an imported module

在项目中使用 hideous RoboClaw Python "library",我似乎无法忘记他们将一堆处理与物理硬件接口的函数和全局变量放入文件中的事实。不幸的是,我一直在使用这个库,因为当供应商为他们的主板发布新固件时,他们也会在他们的网站上更新这个文件;将其移植到有用的东西将是一项持续的努力。

当我尝试多次导入它时出现问题,每次我连接到 USB 的板。像这样(概念上)的东西是理想的:

import roboclaw
class Board:
    def __init__(self):
        self.rc = roboclaw

由于 Python 解释器似乎在每次导入时都在内存中维护相同的模块引用,我似乎无法让它创建存在于不同命名空间中的实例,基本上吐出各种 I/O 当所有板错误地分配给同一个 /dev/ttyACM 设备文件时出现冲突错误。我似乎能够获得的最接近的是 this answer provided by Noctis Skytower,但它仍然没有为每个 Board 实例创建单独的名称空间。

此外,我尝试使用 imp(如 this) and importlib (like this)设置动态导入,尽管这两个都无法导入,因为它们无法找到 roboclaw.py 文件在同一目录中。

对于我应该继续的方向有点迷茫,因为我以前从未处理过这个问题。

好的,通常我会 condemn hacks 就像我建议的那样,但似乎你没有太多选择,因为那个包的作者显然不明白的意思object oriented programming,有一种方法可以通过直接调用 FunctionType() 构造函数来创建具有不同全局范围的函数的副本:

from types import FunctionType
from functools import wraps

def copy_func(func,global_namespace):
    "copies a function object with the new specified global scope"
    c = FunctionType(func.__code__,global_namespace,func.__name__,func.__defaults__,func.__closure__)
    try:
        c.__kwdefaults__ = func.__kwdefaults__
    except AttributeError:pass #this is only for python 3
    c = wraps(func)(c) #copy all metadata although there doesn't seem to be any for roboclaw
    return c

这样它根本不会影响模块中的功能,但仍然使用你自己的命名空间,然后你可以用这个来欺骗复制模块:

class CopyScope:
    def __init__(self,module):
        own_scope = self.__dict__
        for name,thing in vars(module).items():
            if isinstance(thing,FunctionType):
                setattr(self, name, copy_func(thing, own_scope))
            else:
                setattr(self, name, thing)
                #you could also do own_scope[name] = .. instead of setattr() but I prefer setattr()

虽然这只对所有模块级函数运行 copy_func 而不是任何可能使用 global 语句的方法,但我想如果作者了解什么方法你就不需要这个了全部.

我能够使用以下代码对此进行测试:

import roboclaw
x = CopyScope(roboclaw)

x.crc_clear()
x.crc_update(4)

print(x._crc)
print(roboclaw._crc) #this will actually raise an error because it isn't defined in the original module yet.