문제 설명
로깅 인스턴스화에 대한 Python 평가 순서입니다. 가져온 모듈이 먼저 평가되는 이유는 무엇입니까? (Python order of evaluation for a logging instantiation. Why do imported modules get evaluated first?)
안녕하세요 고급 질문이 있습니다. 나는 기본적으로 왜 다운스트림 클래스가 먼저 인스턴스화되는 클래스 속성 로그 객체를 가지고 있는지 어려움을 겪고 있습니다. 약 20개의 클래스와 3개의 다른 모듈이 있는 Python 응용 프로그램이 있고 응용 프로그램에서 일부 로깅을 수행합니다. cli에서 로깅 경로를 구성하고 싶지만 처음에는 로깅 경로를 하드 코딩하고 로깅 개체를 인스턴스화했습니다. 그런 다음 하드 코딩된 경로를 사용하여 애플리케이션의 다른 모든 부분에 인스턴스를 로깅합니다. 그래서 처음에는 로깅 경로를 하드 코딩했습니다(단지 app.log
). 이제 사용자가 로깅 경로를 설정할 수 있도록 하려고 합니다. 그래서 내 Log
클래스에는 클래스 변수인 핸들러를 설정하는 싱글톤 방식의 메소드 set_handler
가 있습니다. 한 번만 설정할 수 있다는 점에서 싱글톤 패턴을 따릅니다. 따라서 사용자는 "첫 번째" 매니저. 이후의 로그 인스턴스화를 위해 처리기가 이미 설정된 경우 처리기가 구성되지 않고 동일한 처리기가 사용되며 새 로그 개체만 반환됩니다. 따라서 사용자가 xyz.log
를 전달하면 영원히 설정됩니다. 하지만 처음 실제로 호출될 때 정확히 알 수 없는 것 같습니다. 진입점은 로깅 개체가 처음 인스턴스화될 때와 많이 다릅니다. 로그 인스턴스화를 강제로 시도하고 있지만 파이썬은 첫 번째 인스턴스화를 계속 다른 것으로 유지합니다. 실제로 내가 먼저 실행하려는 클래스에서 가져온 가져온 클래스에서 가져오는 클래스입니다. 그리고 그 클래스(코드는 훨씬 나중까지 도달하지 않음)에는 먼저 인스턴스화되는 로그 개체가 있습니다. 그래서 그 클래스의 로그 객체가 인스턴스화되고 있지만 사용자가 제공한 로그 경로는 아직 전달되지 않았습니다. 이것이 제 의도입니다. 따라서 기본적으로 python이 적절한 로그 인스턴스화를 먼저 평가하도록 강제할 수 없습니다.
참조 솔루션
방법 1:
So what was happening was basically here, the logging object was being evaluated as soon as the class of the class attribute was imported. (When Class2
was imported, it's class attribute log
was evaluated and thus instantiated). This guy did a blog about it ‑ https://chase‑seibert.github.io/blog/2012/01/20/python‑class‑attributes‑are‑evaluated‑on‑declaration.html. So to lay it out in more detail I had one file like
import other_stuff
from module2 import Class2
class Class1:
def init(self):
do stuff
def func1():
do stuff with Class2
</code></pre>
And then in module 2 it looked like
import os
import subprocess
import other_stuff
class Class2:
log = LoggerClass(log_path=__name__)
def __init__(self):
def func2():
do stuff
And in the application entry point it was like so. This was the part that I wanted to evaluate first... but it did not evaluate first.
import click
import other_stuff
from module1 import Class1
@click.command("start")
@click.option(‑‑logpath)
def start(logpath):
log = LoggerClass(log_path=__name__)
I was thinking that the log
object in the start
function in the entry point would be the first instantiated but it was the log
object that was a class attribute of Class2
that was instantiated first actually. This is because a class's class attributes are evaluated upon being imported. Like when you import a class, that causes all the class attributes to be evaluated.
So I actually just changed the code in Class2 to be like
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
And then of course I change the statements that do the logging from Class2.log.logstuff()
to Class2.log().logstuff()
. So the calls to log stuff no longer reference the class attribute log
and instead call the class method log()
.
(by uh_big_mike_boi、uh_big_mike_boi)
참조 문서