有几种方法可以实现该目标,每种方法都有其优点和缺点。
(其中一些与其他答案重叠。我无意抄袭,只是为了提供全面的答案)。
方法一:函数应该做到这一点
保证函数只执行一次的一种方法是使函数本身有状态,使其“记住”它已经被调用过。这或多或少是@eestrada 和@qarma 所描述的。
至于实现这一点,我同意 @qarma 的观点,即使用记忆化是最简单、最理想的方法。互联网上有一些简单的 python 记忆装饰器。标准库中包含的一个是functools.lru_cache
。您可以简单地使用它,例如:
@functools.lru_cache
def set_up(): # this is your original set_up() function, now decorated
<...same as before...>
这里的缺点是它可以说不是set_up
维持国家的责任,只是一种职能。有人可以争论这一点should如果被调用两次,则执行两次,并且调用者有责任仅在需要时调用它(如果您确实想运行它两次怎么办)?一般的论点是,函数(为了有用和可重用)不应该对其调用的上下文做出假设。
这个论点在你的情况下有效吗?由您决定。
这里的另一个缺点是,这可以被视为滥用记忆工具。 Memoization是与函数式编程密切相关的工具,应该应用于纯函数。记住一个函数意味着“不需要再次运行它,因为我们已经知道了result”,而不是“不需要再次运行它,因为有一些副作用我们想避免”。
方法二:你认为丑的那个(if __name__=='__main__'
)
您在问题中已经提到的最常见的Python方式是使用臭名昭著的if __name__=='__main__' https://stackoverflow.com/q/419163/2096752构造。
这保证了该函数仅被调用一次,因为它仅从名为的模块中调用__main__
,并且解释器保证您的进程中只有一个这样的模块。
这有效。没有任何并发症或警告。这是在 python 中运行主代码(包括设置代码)的方式。已经被考虑了pythonic只是因为它在 python 中非常常见(因为没有更好的方法)。
唯一的缺点是它可以说是丑陋的(从美观角度来看,而不是从代码质量角度来看)。我承认,在我最初几次看到或写下它时,我也皱起了眉头,但你会越来越喜欢它。
方法 3:利用 python 的模块导入机制
Python 已经有一个缓存机制来防止模块被双重导入。您可以通过在新模块中运行设置代码然后导入它来利用此机制。这与@rll 的答案类似。这很简单,要做到:
# logging_setup.py
from framework import logutils
logutils.set_up()
现在,每个调用者都可以通过导入新模块来运行它:
# foo/daily_report.py
import logging_setup # side effect!
def main():
...
由于模块仅导入一次,set_up
只被调用一次。
这里的缺点是它违反了“显式优于隐式”的原则。 IE。如果你想调用一个函数,就调用它。运行对模块导入时间有副作用的代码并不是一个好习惯。
方法 4:猴子修补
这是迄今为止该答案中最糟糕的方法。不要使用它。但这仍然是完成工作的一种方法。
这个想法是,如果您不希望该函数在第一次调用后被调用,请在第一次调用后对其进行猴子修补(阅读:破坏它)。
from framework import logutils
logutils.set_up_only_once()
Where set_up_only_once
可以这样实现:
def set_up_only_once():
# run the actual setup (or nothing if already vandalized):
set_up()
# vandalize it so it never walks again:
import sys
sys.modules['logutils'].set_up = lambda: None
缺点:你的同事会讨厌你。
tl;dr:
最简单的方法是使用记忆functools.lru_cache
,但它可能不是代码质量方面的最佳解决方案。该解决方案是否足够适合您的情况取决于您。
最安全、最Pythonic的方法,虽然不赏心悦目,但使用if __name__=='__main__': ...
.