2013-07-19 18 views
1

背景: 比方說,我們有打開一個頻繁使用的數據庫連接的功能,東西基本上像下面,但額外的花俏:在Python中,通用函數/對象加倍裝飾器是否可行?

​​

也許有時候,我們要打開兩種連接方式,沿着線:

read_connection = myspecialconnect() 
write_connection = myspecialconnect() 

多麼痛苦 - 我有兩次輸入我的密碼,當所有我想要的是一次同樣的事情。當然,有很多方法可以修改這個例子以避免這種情況 - 例如,可以添加一個參數,如myspecialconnect(multi=True)返回兩個連接而不是一個,或者myspecialconnect(copies=9),如果您想要變得瘋狂,請使用相應的代碼使這一切發生在這一個函數中。但是,這種特殊情況促使我想知道更普遍的應用。

問題:如果我希望能夠從任意函數獲得此功能(返回多個我們想要的副本),該怎麼辦?嗯 - 這可能會很棘手。

首先,只是爲了確認它不工作,我嘗試這樣做:

def doubled(function): 
    def Wrapper(*args, **kwargs): 
     return (function(*args, **kwargs),function(*args, **kwargs)) 
    return Wrapper 

沒關係的,無需用戶輸入的功能;否則,你仍然必須坐在那裏,並連續輸入兩次完全相同的東西。這是很容易解決,但現在你也許能看到這是怎麼回事:

def doubled(function): 
    def Wrapper(*args, **kwargs): 
     result = function(*args, **kwargs) 
     return (result, result) 
    return Wrapper 

這個版本需要用戶輸入只有一次,但它返回相同的參考兩次,使得它無非是不必要的更錯綜複雜的做法foo = bar = object()。 「啊哈!」我說,「也許我應該看看copy模塊。」這是我做的,只是我不太知道它是如何工作尚未...

>>> import copy 
>>> a = (i for i in [1,2]) 
>>> a 
<generator object <genexpr> at 0x03FB0878> 
>>> copy.copy(a) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 96, in copy 
    return _reconstruct(x, rv, 0) 
    File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 329, in _reconstruct 
    y = callable(*args) 
    File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy_reg.py", line 93, in __newobj__ 
    return cls.__new__(cls, *args) 
TypeError: object.__new__(generator) is not safe, use generator.__new__() 
>>> copy.deepcopy(a) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 190, in deepcopy 
    y = _reconstruct(x, rv, 1, memo) 
    File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 329, in _reconstruct 
    y = callable(*args) 
    File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy_reg.py", line 93, in __newobj__ 
    return cls.__new__(cls, *args) 
TypeError: object.__new__(generator) is not safe, use generator.__new__() 

當然,現在我已經在這花了約儘可能多的時間,我可以證明有可能(或更多)小問題,這意味着我非常好奇。這是否可以通過返回任意實例副本的方式來完成,而不會變成一個被迫以各自特殊方式顯式處理數十個案例的怪物?

+0

你想要一個實際的副本,或者你想要一個完整的新實例嗎? – Blaine

+0

理想的'@ doubled'裝飾器的功能是使得'a,b = doubled(getSomething)()'分別等價於'a = getSomething()'和'b = getSomething()'。這意味着新的實例。 – Air

回答

2

沒有通用的方法來做你想做的事。這很簡單,如果你只是想重播第一個函數調用 - 你的第一次嘗試會奏效。不幸的是,重放用戶輸入的要求使事情變得複雜。

首先,你不想要副本。你將如何複製數據庫連接?在網絡連接的另一端,你必須複製一個狀態,你必須選擇新的端口,並且不會因爲擁有相同的狀態和屬性而成爲副本。你想打開一個新的連接,使用與舊的連接相同的參數。

其次,裝飾者無法知道重放哪些輸入。使用相同的參數調用函數兩次很容易。調用一個函數兩次,將第一次調用中的用戶輸入重放到第二次調用中,是凌亂但可能的。但是,如果裝飾器試圖將第一次調用的所有輸入絕對重放到第二次調用中,它最終也會重播數據庫的TCP響應。而不是與數據庫交談並建立連接,第二次調用將與裝飾器通信並返回一個不起作用的連接對象。

而不是試圖加倍myspecialconnect,做一個函數,不需要讀取用戶輸入和加倍。讀取密碼一次,然後將其傳遞給加倍功能。

0

編輯:

這裏,如果我理解你的問題正確,以及一起去一個例子的通用的解決方案。

generator = None 

def getObject(askOnce = None, createFunction = None, *args): 
    global generator 
    if generator is None: 
     askOnceValue = askOnce() 
     generator = getGenerator(askOnceValue, createFunction, *args) 
    return generator.next() 

def getGenerator(askOnceValue, createFunction, *args): 
    while(True): 
     yield createFunction(askOnceValue, *args) 

def myInputFunction(): 
    return "Some dynamic value" 

def myCreateFunction(myInput, arg1, arg2): 
    return list([myInput, arg1, arg2]) 

myObj1 = getObject(myInputFunction, myCreateFunction, "hello", 1234) 

print(myObj1) 

myObj2 = getObject() 

print(myObj2) 

這可以通過創建一個類CopyableObject(可怕的名字..),然後擴展它更好。

+0

關鍵是要*不*必須重新輸入密碼。 – user2357112

+0

是的,現在我已經修復了我的錯誤,您不必這樣做。 – Blaine

+0

這很有趣,但沒有回答我問的問題。我會給OP增加一些重點來澄清這個問題。 – Air

相關問題