Python 日志实例化的评估顺序。为什么首先评估导入的模块?

Python order of evaluation for a logging instantiation. Why do imported modules get evaluated first?

您好,我有一个高级问题。我基本上很难理解为什么下游 class 有一个 class 属性日志对象首先被实例化。我有一个 python 应用程序,其中包含大约 20 个 classes 和 3 个不同的模块,并且该应用程序进行了一些日志记录。我想让日志记录路径可从 cli 配置,但起初,我只是硬编码日志记录路径并实例化一个日志记录对象。然后使用硬编码路径将该日志实例记录到应用程序的所有其他部分。所以我首先硬编码了日志记录路径(只是 app.log)。现在我试图让用户设置日志记录的路径。所以在我的 Log class 中,我有一个设置处理程序的单例方法 set_handler,它是一个 class 变量。它遵循单例模式,因为它只能设置一次。因此,用户还将为“第一个”处理程序传递一个日志记录路径。如果已经为以后的日志实例化设置了处理程序,则不配置任何处理程序,使用相同的处理程序,只返回一个新的日志对象。因此,如果用户传入 xyz.log 那么它将永远设置,很好。但似乎我无法确定 第一次 实际调用的时间。入口点与第一次实例化日志对象有很大不同。我试图强制日志实例化,但 python 一直将第一个实例化为不同的东西。它实际上是由 class 导入的 class 导入的 class ,我实际上首先要执行的是 class 导入的。 class(代码直到很晚才命中)有一个日志对象,它首先被实例化。因此 class 的日志对象正在被实例化,但用户提供的日志路径尚未通过,这是我的意图。所以基本上我无法强制 python 首先评估正确的日志实例化。

所以发生的事情基本上就在这里,在导入 class 属性的 class 后立即评估日志记录对象。 (当 Class2 被导入时,它的 class 属性 log 被评估并因此被实例化)。这家伙写了一篇关于它的博客 - https://chase-seibert.github.io/blog/2012/01/20/python-class-attributes-are-evaluated-on-declaration.html。所以为了更详细地展示它,我有一个文件

import other_stuff   
 
from module2 import Class2 

class Class1:
    def __init__(self):
            do stuff 
    
    def func1():
        do stuff with Class2 

然后在模块 2 中看起来像

import os 
import subprocess 

import other_stuff 

class Class2:
    log = LoggerClass(log_path=__name__)
      
    def __init__(self):

    def func2():
        do stuff  

在应用程序入口点,它是这样的。这是我想先评价的部分。。。但是没有先评价。

import click 
import other_stuff 

from module1 import Class1

@click.command("start")
@click.option(--logpath)
def start(logpath):
    log = LoggerClass(log_path=__name__)

我在想入口点的 start 函数中的 log 对象将是第一个实例化的对象,但 log 对象是 class Class2 的属性实际上首先被实例化了。这是因为 class 的 class 属性在导入时进行评估。就像当您导入 class 时,这会导致所有 class 属性被计算。

所以我实际上只是将 Class2 中的代码更改为

import os 
import subprocess 

import other_stuff 

class Class2:
    log = None
      
    def __init__(self):

    def func2():
        do stuff 

    @classmethod
    def log(cls):
        if cls.log is None:
             cls.log = LoggerClass(log_name=__name__)
        return cls.log

当然,我将执行日志记录的语句从 Class2.log.logstuff() 更改为 Class2.log().logstuff()。因此,对日志内容的调用不再引用 class 属性 log,而是调用 class 方法 log().