猴子补丁 logging.Logger -- 参数是如何工作的?

0 投票
1 回答
1460 浏览
提问于 2025-04-18 12:18

我写了一个日志记录的类,挺喜欢用的。它看起来是这样的:

class EasyLogger(object):
    SEP = " "

    def __init__(self, logger=logging.getLogger(__name__)):
        self.logger = logger

    def _format_str(self, *args):
        return self.SEP.join([str(a) for a in args])

    def debug(self, *args):
        self.logger.debug(self._format_str(*args))

    .... repeated for the other logging methods, like info, warning, etc....

    def __getattr__(self, name):
        return getattr(self.logger, name)

这样我就能用上这样的语法:

LOG.debug("some_variable: ", some_variable)

我觉得这个挺好的。我不想从 logging.Logger 继承,因为组合比继承要好,而且我也不想去碰那些内置的类。

我的格式字符串是这样的:

LOGGING_FMT = "<%(filename)s:%(lineno)s(%(levelname)s) - %(funcName)s() >"\
                        "%(message)s"

但这个不太管用,因为行号和函数名总是显示为 easylogger.py 里面的东西,而不是调用这个函数的地方。

我通过参考 这个 很棒的答案,修复了这个问题,方法是对 EasyLogger.loggerfindCaller 方法进行了猴子补丁,代码如下:

def __init__(self, logger=logging.getLogger(__name__)):
    self.logger = logger
    # Ugly, ugly, ugly dirty hack to fix line numbers
    self.logger.findCaller = find_caller_monkeypatch

这样可以正常工作,但我有点困惑。我最开始定义 find_caller_monkeypatch 的时候,参数是这样的:

def find_caller_monkeypatch(self):

(函数的主体是从链接的答案中复制过来的)。不过,这样会导致一个错误:

  File "/usr/lib64/python2.7/logging/__init__.py", line 1262, in _log
    fn, lno, func = self.findCaller()
TypeError: find_caller_monkeypatch() takes exactly 1 argument (0 given)

去掉 find_caller_monkeypatchself 参数后,错误就解决了,但我还是不明白:为什么在调用 self.findCaller() 时,find_caller_monkeypatch 没有接收到 self 作为参数?方法只有在类里面定义时才会接收 self 吗?

下面的代码片段是同样问题的一个小例子:

In [7]: class Foo(object):
   ...:     def say_hi(self):
   ...:         print "Hello World"
   ...:         

In [8]: def say_bye_self(self): print "Goodbye world"

In [9]: def say_bye(): print "Goodbye world"

In [10]: foo = Foo()

In [11]: foo.say_hi()
Hello World

In [12]: foo.say_hi = say_bye_self

In [13]: foo.say_hi()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-10baea358e0b> in <module>()
----> 1 foo.say_hi()

TypeError: say_bye_self() takes exactly 1 argument (0 given)

In [14]: foo.say_hi = say_bye

In [15]: foo.say_hi()
Goodbye world

这是怎么回事呢?

1 个回答

2

你可以对这个类进行猴子补丁(Monkeypatch)

Foo.say_hi = say_bye_self
foo = Foo()
foo.say_hi()

或者你也可以对某个实例进行猴子补丁

import types
foo.say_hi = types.MethodType(say_bye_self, foo)

撰写回答