2011-11-05 78 views
5

我正在編寫一個模塊,它提供了一個函數並需要一個初始化步驟,但由於某些限制,我需要在第一次調用時進行初始化,所以我正在尋找適當的Python語言成語,我擺脫了條件。更改Python中的函數實現

#with conditional 
module.py 
initialized = False 
def function(*args): 
    if not initialized: initialize() 
    do_the_thing(*args) 

我想擺脫有條件的像這樣的東西(它不工作):

#with no conditional 
module.py 
def function(*args): 
    initialize() 
    do_the_thing(*args) 
    function = do_the_thing 

我意識到,我不能只在模塊中使用的名稱,並在改變他們因爲使用from module import function的模塊不會受到模塊內function=other_fun的影響。
那麼,是否有任何pythonic成語可以做到這一點正確的方式?

+0

谷歌爲「蟒蛇裝飾」 – Triptych

+0

我沒有看到一個裝飾如何能做到這一點,條件仍然存在,如果你測試@graphox解決方案,使用一個生成器按預期工作,雖然它感覺有點奇怪。 –

回答

8

的沒什麼花哨的方式(我張貼在這裏的方法,這可能是做的最好的方法):

module.py:

def initialize(): 
    print('initialize') 
def do_the_thing(args): 
    print('doing things',args) 
def function(args): 
    _function(args) 
def firsttime(args): 
    global _function 
    initialize() 
    do_the_thing(args) 
    _function=do_the_thing 
_function=firsttime 

的想法很簡單:你只需添加一個間接層。 function始終呼籲_function,但_function首先在firsttime,然後永遠在do_the_thing後。

test.py:

from module import function 
function(1) 
function([2,3]) 

運行test.py產量

initialize 
('doing things', 1) 
('doing things', [2, 3]) 

我首先想到的是使用一個發電機,但是,作爲三聯指出,有如果您使用生成器,則無法將args傳遞給該函數。所以......

這裏是使用協同程序(它不同於發電機,允許你發送參數傳遞給 - 以及從收到值 - 協程)的方式:

module.py :

def coroutine(func): 
    # http://www.dabeaz.com/coroutines/index.html 
    def start(*args,**kwargs): 
     cr = func(*args,**kwargs) 
     cr.next() 
     return cr 
    return start 

def initialize(): 
    print('initialize') 

def do_the_thing(*args, **kwargs): 
    print('doing things', args, kwargs) 
    return ('result', args) 

@coroutine 
def _function(): 
    args, kwargs = (yield) 
    initialize() 
    while True: 
     args, kwargs = (yield do_the_thing(*args, **kwargs)) 
_function = _function().send 
def function(*args, **kwargs): 
    # This is purely to overcome the limitation that send can only accept 1 argument 
    return _function((args,kwargs)) 

運行

print(function(1, x = 2)) 
print(function([2, 3])) 

產生

initialize 
('doing things', (1,), {'x': 2}) 
('result', (1,)) 
('doing things', ([2, 3],), {}) 
('result', ([2, 3],)) 
+0

這將在模塊加載時運行初始化步驟,而不是在OP請求時首先調用... – Triptych

+0

@Triptych:我不相信這是真的。你測試過了嗎? – unutbu

+0

啊誤讀'next'就好像它是'next()'。我的壞...仍然好奇你會如何寫論據。 'next'不是一個零參數函數嗎?似乎...對我來說很奇怪。 – Triptych

2

你也可以使用一個裝飾,它是如果你有幾個函數初始化也許更靈活:

import functools  

def initialize(initialize_function): 
    def wrap(fn): 
     fn.initialized = False 
     @functools.wraps(fn) 
     def wrapper(*args, **kwargs): 
      if not fn.initialized: 
       initialize_function() 
       fn.initialized = True 
      return fn(*args, **kwargs) 
     return wrapper 
    return wrap 

def initialize_first_fn(): 
    print('first function initalized') 

def initialize_second_fn(): 
    print('second function initalized') 

@initialize(initialize_first_fn) 
def first_fn(*args): 
    print(*args) 

@initialize(initialize_second_fn) 
def second_fn(*args): 
    print(*args) 


>>>first_fn('initialize', 'please') 
first function initalized 
initialize please 
>>> first_fn('it works') 
it works 
>>> second_fn('initialize', 'please') 
second function initalized 
initialize please 
>>> second_fn('it also works') 
it also works 

(需要根據您的需求來加以改進)

+0

嗯,包裝代碼是否執行每個呼叫?我的意思是,有條件的人仍然會在第二次電話會議上,只是評估爲虛假,不是嗎? –

+1

我會在'def wrapper'上使用'@ functools.wrap(fn)'。這樣你得到的函數看起來像原始函數('first_fn'等)。我可能也會使用基於類的修飾器。 –

+0

我剛剛測試過它,事實上,「如果未初始化」仍會發生每次調用,這是我想要刪除的。 :( –

3

我對這個看法:你不應該這樣做。

如果您需要一個具有「初始化步驟」和正常工作模式的「函數」,則需要一個類實例。不要試圖聰明,你的代碼的未來讀者會恨你說:)

# module.py 

class ThingDoer(object): 
    def __init__(self): 
     # initialize 

    def do_the_thing(self, *args): 
     # ...