在一个模块中更改模块变量,而在原始模块中不会更改

时间:2012-01-20 00:26:43

标签: python scope python-module

我正在使用foo模块,其中包含一些指示其行为的模块级变量。其中一个变量是SANITIZE。

我希望能够以两种方式使用foo,foo.parse()和foo2.parse(),其中唯一的区别是foo有SANITIZE = false而foo2有SANITIZE = true

我想这样做,而不必复制粘贴我的代码。例如

#foo module
SANITIZE='foo'
def parse():
    print SANITIZE

#foo2 module
import foo
foo.SANITIZE='foo2'


#test script
import foo
import foo2
foo2.foo.parse() #i want this to print 'foo2'
foo.parse() #i want this to print 'foo'

但是,上面的例子将同时打印'foo2'。有没有办法实现这种行为?

谢谢!

2 个答案:

答案 0 :(得分:3)

如果这是你的代码,那么解决方案不是依赖于模块级变量,而是以某种其他方式保持对象的状态。 Python中的模块是“单例” - 这意味着一旦被任何模块导入,那么只有一个版本,广泛的解释器 - 例如,你想要的行为就是免费的,如果你使用类继承 - 子类可以自定义一些,但不需要重写所有的父类。

因此,如果您将类似的“foo”代码封装在一个类中 - 您甚至可能不必编写需要实例的类,那么您将获得所需的功能:

#foo module:

class Foo(object):
    sanitize = "value 1"
    @classmethod
    def parse(cls):
        print cls.sanitize

#foo2
from foo import Foo
class Foo2(Foo):
    sanitize = "value 2"

# code:
from foo import Foo
from foo2 import Foo2

Foo.parse()
Foo2.parse()

当然,随着Python允许的内省和元编程的欢呼, 可能会做出你想要的事情 - 但这会使问题变得复杂,没有好处。模块变量与C代码中的全局变量具有大多数相同的缺点。

一种方法是在通过foo2访问时使“foo”动态变化,在“foo”中调用函数并在退出时恢复previosu值。 对于在模块“foo2”上对“foo”的属性访问触发的任意代码,“foo”必须引用具有“property”属性的对象 -

所以,你编写的exa示例将以一种不安全的方式运行,顺便说一句,非常不安全的方式,如果foo2或多或少写得如此:

import foo as _foo

SANITIZE = "value2"

class _FooWrapper(object):
    def __getattribute__(self, attr):
        self.change_vars()
        original_function = getattr(_foo, attr)
        if callable(original):
            def wrapper(func):
                def new_func(*args, **kw):
                    res = func(*args, **kw)
                    self.restore_vars()
                    return res
                return new_func
            return wrapper(original)
        return original

    def change_vars(self):
        self.original_sanitize = _foo.SANITIZE
        _foo.SANITIZE = SANITIZE
    def restore_vars(self):
        __foo.SANITIZE = self.original_sanitize

foo = _FooWrapper()

这会在模块foo2中创建一个“foo”对象,当访问它时,会从原始的“foo”模块中检索任何请求的属性。因此,“foo2.foo.parse”将获得“foo”解析函数 - 但是,在这种方法中感知“hackiness”的大部分 - 为了能够恢复生活在其中的“foo”模块中的原始值。 Python解释器,在从foo2获取函数之后,该函数也会在返回时自行恢复值。唯一的方法是修改函数,以便在返回时运行其他代码 - 因此它可以通过上面的代码动态修饰。

我认为这个例子清楚地表明,虽然可能,但在这种情况下,模块级配置不是可行的方法。

修改 O.P.评论说:

  

谢谢jsbueno,遗憾的是这不是我的代码,我必须依赖   模块级变量。 Wrapper类方法很有趣   但正如你所说,非常hacky和令人难以置信的非线程安全,我是   害怕不会为我的情况做什么

回复:

  

模块是“单例” - 所以,更改模块上的变量,at   任何一点都会使它不安全。我能想到的另一种方式   这是创建一个实际重新创建的“复印机”模块   类,属性和另一个现有模块的实例,何时   import - 重新绑定所有函数全局变量(方法会   仍然在这个级别上被视为功能)

阅读此描述可能听起来“不可行” - 但它比描述的更容易完成 - 以下是执行上述操作的“foo2”模块:

from types import ModuleType, FunctionType

import foo as _foo

SANITIZE = "value 2"

def rebuild_function(func, glob):
    """Rebinds the globals in the given functions to globals in 
    this module  by default"""
    new_func = FunctionType(func.func_code,
                            glob,
                            func.func_name,
                            func.func_defaults,
                            func.func_closure)
    return new_func

def rebuild_class(cls, glob):
    metatype = type(cls)
    dct = cls.__dict__.copy()
    for key, value in dct.items():
        if isinstance(value, FunctionType):
            dct[key] = rebuild_function(value, glob)
    return metatype(cls.__name__, cls.__bases__, dct)

def rebuild_module(mod,glob):
    new_module = ModuleType(mod.__name__)
    for key, value in mod.__dict__.items():
        if isinstance(value, FunctionType):
            value = rebuild_function(value, glob)
        elif isinstance(value, type):
            value = rebuild_class(value, glob)
        setattr(new_module, key, value)
    return new_module

foo = rebuild_module(_foo, globals())

__all__ = ["foo", "SANITIZE"]

这段代码正是我所描述的 - 它重新创建原始模块中的所有函数对象,重新设置每个函数的全局字典。它是并发安全的。如果要被克隆的模块确实指向本机代码类或函数(它们不是“FunctionType”),则存在一些极端情况。如果它大量使用多类继承,元类, - 它是否有效。

我用一个简单的类测试它并且它运行良好:

#module "foo"
SANITIZE='foo'
def parse():
    print SANITIZE

class Parser(object):
    def __init__(self):
        print SANITIZE * 2

#test script
import foo
import foo2
foo2.foo.parse() #i want this to print 'foo2'
foo2.foo.Parser()
foo.parse() #i want this to print 'foo'
foo.Parser()

输出:

[gwidion@powerpuff tmp16]$ python test_foo.py 
foofoo
value 2
value 2value 2
foo
foofoo
[gwidion@powerpuff tmp16]$ 

答案 1 :(得分:-1)

您的问题与此处描述的内容非常相似:http://effbot.org/pyfaq/how-do-i-share-global-variables-across-modules.htm

File: config.py (your foo.py)
x = 0   # Default value of the 'x' configuration setting

File: mod.py (your foo2.py)
import config
config.x = 1

File: main.py
import config
import mod
print config.x # Prints 1

根据您的情况,您可以执行以下操作:

#foo module
SANITIZE = 'foo'
def parse():
    print SANITIZE

#foo2 module
import foo
foo.SANITIZE = 'foo2'

#test script 1
import foo
foo.parse() # Prints 'foo'

#test script 2
import foo
import foo2
foo.parse() # Prints 'foo2'