更改Python中的函数实现

问题描述:

我正在编写一个模块,它提供了一个函数并需要一个初始化步骤,但由于某些限制,我需要在第一次调用时进行初始化,所以我正在寻找适当的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解决方案,使用一个生成器按预期工作,虽然它感觉有点奇怪。 –

的没什么花哨的方式(我张贴在这里的方法,这可能是做的最好的方法):

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

你也可以使用一个装饰,它是如果你有几个函数初始化也许更灵活:

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

我刚刚测试过它,事实上,“如果未初始化”仍会发生每次调用,这是我想要删除的。 :( –

我对这个看法:你不应该这样做。

如果您需要一个具有“初始化步骤”和正常工作模式的“函数”,则需要一个类实例。不要试图聪明,你的代码的未来读者会恨你说:)

# module.py 

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

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