【问题标题】:Multiple instances of a global module variable?全局模块变量的多个实例?
【发布时间】:2013-07-30 20:09:41
【问题描述】:

背景:我正在使用设备供应商提供的 API 模块,该模块将设备登录数据(设备主机名、会话 ID 等)存储为全局变量;我试图弄清楚是否可以有多个模块实例来表示对多个设备的登录。

到目前为止,我已经用测试代码尝试了几种策略,但都没有奏效:

测试模块代码:statictest.py

count = 0

class Test():
  @classmethod
  def testcount(cls):
    global count
    count += 1
    return count

第一次尝试:多次导入模块并实例化:

>>> import statictest as s1
>>> import statictest as s2
>>> s1.Test.testcount()
1 
>>> s1.Test.testcount()
2
>>> s2.Test.testcount()
3

第二次尝试:在类中导入模块,实例化类:

#!/usr/bin/env python2.7
class TestMod():
  s = __import__('statictest')

  def test(self):
    ts = self.s.Test()
    return ts.testcount()

t = TestMod()
u = TestMod()
print t.test()
print u.test()

那个也不行:

[~/]$ ./moduletest.py 
1
2

这似乎应该很明显,但是有没有办法封装一个模块以便多个实例可用?

【问题讨论】:

  • 没有。如果你想要多个实例,你应该使用一个类,而不是一个模块级的值。通过将此状态置于模块级别,您的供应商行为(非常)糟糕。
  • 请向您的供应商提交错误。这是被设计破坏的。

标签: python


【解决方案1】:

以下似乎有效。它使用您的 statictest.py 模块以及其他答案中的一些想法的组合来创建一个上下文管理器,该管理器将允许轻松切换和使用模块的任何各种实例:

from contextlib import contextmanager
import importlib
import random
import sys

MODULE_NAME = 'statictest'
NUM_INSTANCES = 4
instances = []

# initialize module instances
for _ in xrange(NUM_INSTANCES):
    if MODULE_NAME in sys.modules:
        del sys.modules[MODULE_NAME]

    module = importlib.import_module(MODULE_NAME)
    for _ in xrange(random.randrange(10)): # call testcount a random # of times
        module.Test.testcount()

    instances.append(sys.modules[MODULE_NAME])

@contextmanager
def statictest_inst(n):
    save = sys.modules[MODULE_NAME]
    sys.modules[MODULE_NAME] = instances[n]
    yield instances[n]
    sys.modules[MODULE_NAME] = save

def get_counts():
    counts = []
    for i in xrange(NUM_INSTANCES):
        with statictest_inst(i) as inst:
            counts.append(inst.count)
    return counts

print 'initial counts', get_counts()

choice = random.randrange(NUM_INSTANCES)
print 'calling instance[{}].testcount()'.format(choice)
with statictest_inst(choice) as inst: # use context manager
    inst.Test.testcount()

print 'counts after updating one of them', get_counts()

样本输出:

initial counts [2, 4, 4, 1]
calling instance[2].testcount()
counts after updating one of them [2, 4, 5, 1]

【讨论】:

    【解决方案2】:

    我认为这是不可能的,因为 Python 模块的行为非常像单例(实际上这是在 Python 中创建单例的有效方法)。例如Refer to this SO threadthis one。这种设计旨在防止同一模块的多次导入,因为它可能有点昂贵。 logging 模块就是一个很好的例子。您设置了一次记录器,所有由同一个解释器运行并导入 logging 的代码都将写入同一个日志文件。

    【讨论】:

      【解决方案3】:

      在导入之间,您可以从sys.modules 中删除模块以强制重新导入:

      import sys
      import module
      del sys.modules['module']
      import module as module2
      print(module is module2) # prints False
      

      【讨论】:

        【解决方案4】:

        如果您复制将起作用的文件(statictest1.py、statictest2.py):

        >>> import sttest1 as s1
        >>> import sttest2 as s2
        >>> s1.Test.testcount()
        1
        >>> s1.Test.testcount()
        2
        >>> s1.Test.testcount()
        3
        >>> s2.Test.testcount()
        1
        >>> s2.Test.testcount()
        2
        >>> s2.Test.testcount()
        

        【讨论】:

        • 注意:如果第二个文件只是指向第一个文件的链接,则此方法有效,因此您可以动态创建链接,然后导入“新”文件。有点奇怪,但似乎有效。
        【解决方案5】:

        如果模块的状态真的被很好地包含,你可以编写一个上下文管理器来在你调用它时交换全局状态进出模块(即猴子补丁它)。

        例如:

        import mymodule
        
        class ModuleState(object):
            def __init__(self):
                self.global_state = mymodule.global_state
        
            def __enter__(self):
                my_module.global_state = self.global_state
                return self
        
            def __exit__(self, type, value, traceback):
                my_module.global_state = default_state
        
        default_state = mymodule.global_state
        
        # Init mymodule for state1
        state1 = ModuleState()
        
        # Init mymodule for state2
        state2 = ModuleState()
        
        # Init mymodule for state3
        state3 = ModuleState()
        
        
        # Do something in state2
        with state2:
            mymodule.something()
        

        【讨论】:

          猜你喜欢
          • 2016-02-10
          • 2015-03-07
          • 2013-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-20
          • 2016-09-15
          • 2017-07-26
          相关资源
          最近更新 更多