更改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成语可以做到这一点正确的方式?
的没什么花哨的方式(我张贴在这里的方法,这可能是做的最好的方法):
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],))
你也可以使用一个装饰,它是如果你有几个函数初始化也许更灵活:
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
(需要根据您的需求来加以改进)
嗯,包装代码是否执行每个呼叫?我的意思是,有条件的人仍然会在第二次电话会议上,只是评估为虚假,不是吗? –
我会在'def wrapper'上使用'@ functools.wrap(fn)'。这样你得到的函数看起来像原始函数('first_fn'等)。我可能也会使用基于类的修饰器。 –
我刚刚测试过它,事实上,“如果未初始化”仍会发生每次调用,这是我想要删除的。 :( –
我对这个看法:你不应该这样做。
如果您需要一个具有“初始化步骤”和正常工作模式的“函数”,则需要一个类实例。不要试图聪明,你的代码的未来读者会恨你说:)
# module.py
class ThingDoer(object):
def __init__(self):
# initialize
def do_the_thing(self, *args):
# ...
谷歌为“蟒蛇装饰” – Triptych
我没有看到一个装饰如何能做到这一点,条件仍然存在,如果你测试@graphox解决方案,使用一个生成器按预期工作,虽然它感觉有点奇怪。 –