2012-01-20 127 views
0

我正在使用包含幾個指示其行爲的模塊級變量的foo模塊。其中一個變量是SANITIZE。在一個模塊中更改模塊變量,但它在原始模塊中沒有更改

我希望能夠以兩種方式使用FOO,foo.parse()和foo2.parse(),其中唯一的區別是FOO具有消毒=虛假和foo2的具有消毒=真

我想做到這一點,而不必複製粘貼我的代碼。例如,

#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

這個代碼是沒有意義的,foo.parse ()在任何情況下都會打印'foo',這是一個不以任何方式依賴於SANITIZE的常量。 – yurib

+0

對於那個yurib抱歉,我解決了它。 – djs22

+0

你總是可以刪除並重新加載foo.py –

回答

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」中調用函數並在退出時恢復先前的值。 對於在模塊「foo2」上的「foo」屬性訪問上觸發的任意代碼,「foo」必須引用具有「屬性」屬性的對象 - 因此,您編寫的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」的對象,獲取任何請求的屬性從原來的「富」模塊。因此,「foo2.foo.parse」將獲得「foo」解析函數 - 但是,在這種方法中,感知「hackiness」的數量 - 爲了能夠恢復生活在「foo」模塊中的原始值Python解釋器,在從foo2獲取函數之後,該函數也會返回,自己恢復這些值。唯一的方法是修改該函數,以便它在返回時運行附加代碼 - 因此它由上面的代碼實時裝飾。

我認爲這個例子清楚地表明,在這種情況下使用模塊級配置並不是可行的方法。

編輯 的O.P.評論:

感謝jsbueno,遺憾的是這不是我的代碼,我必須依靠 模塊級變量。包裝類方法是有趣 但正如你所說,非常哈克和令人難以置信的非線程安全的,這我恐怕 不會爲我的情況

在回答該做的事:

模塊是「單身人士」 - 因此,更改模塊上的變量,在 任何一點都會使其線程不安全。另一種方式,我可以考慮 這是創建一個「複印機」模塊,實際上重新創建 類別,屬性和實例的另一個現有的模塊,當 導入 - 重新綁定所有函數的全局變量(方法將 仍然被處理爲在這個級別的功能)

閱讀本說明書聽起來作爲「unfeasable」 - 但它不是容易完成的描述 - 這裏緊跟我的「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」),則存在一些轉角情況。如果它大量使用多個類繼承,metaclasses, - 它有用或不可用。

我用一個簡單的類測試,它工作得很好:

#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() 

輸出:

[[email protected] tmp16]$ python test_foo.py 
foofoo 
value 2 
value 2value 2 
foo 
foofoo 
[[email protected] tmp16]$ 
+0

謝謝jsbueno,不幸的是這不是我的代碼,我必須依靠模塊級別的變量。包裝類的方法很有趣,但正如你所說,非常hacky和令人難以置信的非線程安全,恐怕不會爲我的情況做 – djs22

+0

模塊是「單身」 - 所以,在任何時候改變模塊上的變量會使它不安全。我可以考慮的另一種方式是創建一個「複印機」模塊,當導入時實際上重新創建另一個現有模塊的類,屬性和實例 - 重新綁定所有函數全局變量(方法仍將作爲此級別的函數進行處理)。 – jsbueno

+0

是的,「複印機」模塊正是我需要的,我希望這樣的事情已經存在 – djs22

-1

你的問題看起來非常相似,這裏所描述'S: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' 
+0

謝謝sgallen,不幸的是,這並不完全符合我的要求。 你的代碼不允許我在同一個腳本內的foo和foo2之間切換 – djs22