登录框架 [英] Logging in a Framework

查看:78
本文介绍了登录框架的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下,有一个框架提供了称为logutils.set_up()的方法,该方法根据某些配置来设置日志记录.

Imagine there is a framework which provides a method called logutils.set_up() which sets up the logging according to some config.

应尽早设置日志记录,因为导入库期间发出的警告不应丢失.

Setting up the logging should be done as early as possible since warnings emitted during importing libraries should not be lost.

由于旧的方式(if __name__=='__main__':)看起来很丑,所以我们使用console_script入口点来注册main()方法.

Since the old way (if __name__=='__main__':) looks ugly, we use console_script entrypoints to register the main() method.

# foo/daily_report.py
from framework import logutils
logutils.set_up()
def main():
    ...

我的问题是logutils.set_up()可能被调用两次:

My problem is that logutils.set_up() might be called twice:

想象一下,还有另一个控制台脚本,它调用logutils.set_up()imports daily_report.py.

Imagine there is a second console script which calls logutils.set_up() and imports daily_report.py.

在第二次调用logutils.set_up()时,我可以更改框架代码和set_up()不执行任何操作,但这感觉很笨拙.我想避免这种情况.

I can change the framework code and set_up() to do nothing in the second call to logutils.set_up(), but this feels clumsy. I would like to avoid it.

如何确定logutils.set_up()仅执行一次?

推荐答案

有几种实现目标的方法,每种方法都有其优点和缺点.

(其中一些与其他答案重叠.我并不是要窃,只是为了提供全面的答案).

方法1:该功能应该执行

保证函数仅执行一次的一种方法是使函数本身成为有状态的,使其记住"已被调用.这或多或少是@eestrada和@qarma所描述的.

One way to guarantee a function only gets executed once, is to make the function itself stateful, making it "remember" it has already been called. This is more or less what is described by @eestrada and @qarma.

关于实现这一点,我同意@qarma的观点,即使用记忆是最简单,最意识形态的方法.互联网上有一些用于python的简单的备注装饰器.标准库中包含的是functools.lru_cache.您可以像这样简单地使用它:

As to implementing this, I agree with @qarma that using memoization is the simplest and most ideomatic way. There are a few simple memoization decorators for python on the internet. The one included in the standard library is functools.lru_cache. You can simply use it like:

@functools.lru_cache
def set_up():  # this is your original set_up() function, now decorated
    <...same as before...>

此处的缺点是,可以说不是维持状态的set_up责任,而仅仅是功能.有人说如果被两次调用,它应该执行两次,而调用者有责任仅在需要时调用它(如果您确实想两次运行它,该怎么办)?一般的论点是,函数(为了有用和可重用)不应对调用它的上下文进行假设.

The disadvantage here is that it is arguably not the set_up's responsibility to maintain the state, it is merely a function. One can argue it should execute twice if being called twice, and it's caller's responsibility to only call it when it needs it (what if you really do want to run it twice)? The general argument is that a function (in order to be useful and reusable) should not make assumptions about the context in which it is called.

此论点在您的情况下是否有效?由您决定.

Is this argument valid in your case? It is up to you to decide.

这里的另一个缺点是,这可以被认为是对备忘录工具的滥用.记忆化是与函数编程紧密相关的工具,应应用于纯函数.记忆功能意味着无需再次运行它,因为我们已经知道了结果",而不是无需再次运行它,因为存在一些副作用"想避免".

Another disadvantage here is that this can be cosidered an abuse of the memoization tool. Memoization is a tool closely related to functional programming, and should be applied to pure functions. Memoizing a funciton implies "no need to run it again, because we already know the result", and not "no need to run it again, because there's some side effect we want to avoid".

方法2:您认为丑陋的方法(if __name__=='__main__')

Approach 2: the one you think is ugly (if __name__=='__main__')

您已经在问题中提到的最常见的pythonic方法是使用臭名昭著的 if __name__=='__main__' 构造.

The most common pythonic way, which you already mention in your question, is using the infamous if __name__=='__main__' construct.

这保证了该函数仅被调用一次,因为它只能从名为__main__的模块中调用,并且解释器保证您的进程中只有一个这样的模块.

This guarantees the function is only called once, because it is only called from the module named __main__, and the interpreter guarantees there is only one such module in your process.

这有效.没有并发症,也没有警告.这是在python中运行主代码(包括设置代码)的方式.之所以将其视为 pythonic 仅仅是因为它在python中是如此常见(因为没有更好的方法).

This works. There are no complications nor caveats. This is the way running main-code (including setup code) is done in python. It is considered pythonic simply because it is so darn common in python (since there are no better ways).

唯一的缺点是,它可以说是丑陋的(从美学角度而言,而不是从代码质量角度而言).我承认我也看到或写过它的前几次也有些畏缩,但它会在您身上成长.

The only disadvantage is that it is arguably ugly (asthetics-wise, not code-quality-wise). I admit I also winced the first few times I saw it or wrote it, but it grows on you.

方法3:利用python的模块导入机制

Python已经具有一种缓存机制,可防止模块被双重导入.您可以通过在新模块中运行安装代码,然后将其导入来利用此机制.这类似于@rll的答案.这很容易做到:

Python already has a caching mechanism preventing modules from being doubly-imported. You can leverage this mechanism by running the setup code in a new module, then import it. This is similar to @rll's answer. This is simple, to do:

# logging_setup.py
from framework import logutils
logutils.set_up()

现在,每个调用者都可以通过导入新模块来运行它:

Now, each caller can run this by importing the new module:

# foo/daily_report.py
import logging_setup # side effect!
def main():
    ...

由于模块仅导入一次,因此set_up仅被调用一次.

Since a module is only imported once, set_up is only called once.

此处的缺点是它违反了显式优于隐式"原则. IE.如果要调用函数,请调用它.在模块导入时间上运行具有副作用的代码不是一种好习惯.

The disadvantage here is that it violates the "explicit is better than implicit" principle. I.e. if you want to call a function, call it. It isn't good practice to run code with side-effects on module-import time.

方法4:猴子修补

到目前为止,这是此答案中最差的一种方法.不要使用它.但这仍然是完成工作的一种方式.

This is by far the worst of the approaches in this answer. Don't use it. But it is still a way to get the job done.

这个想法是,如果您不希望在第一次调用之后调用该函数,请在第一次调用之后进行猴子补丁(阅读:破坏它).

The idea is that if you don't want the function to get called after the first call, monkey-patch it (read: vandalize it) after the first call.

from framework import logutils
logutils.set_up_only_once()

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

缺点:您的同事会恨您.

Disadvantages: your colleagues will hate you.

tl; dr:

最简单的方法是使用functools.lru_cache进行记忆,但这可能不是最佳的代码质量解决方案.这种解决方案在您的情况下是否足够好取决于您自己.

The simplest way is to memoize using functools.lru_cache, but it might not be the best solution code-quality-wise. It is up to you if this solution is good enough in your case.

最不安全,最Python化的方式是使用if __name__=='__main__': ...,虽然不令人赏心悦目.

The safest and most pythonic way, while not pleasing to the eye, is using if __name__=='__main__': ....

这篇关于登录框架的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆