我正在使用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'。有没有办法实现这种行为?
谢谢!
答案 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'