You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python派生类未覆盖self.logger的原因及解决方法

Python继承体系中派生类logger未按预期覆盖的问题解析与修复

问题原因

  • 初始化执行顺序导致的覆盖时机问题:创建Child实例时,初始化流程是Child.__init__ → Parent.__init__ → Person.__init__。每个子类的__init__都会先调用父类的__init__,再给自身的self.logger赋值。
    • 首先执行Person.__init__,此时self.logger被设置为Person类的logger,随后调用的self.Print()因为Python的多态特性,实际执行的是Child类的Print方法,所以此时打印的是Person的logger地址。
    • 接着执行Parent.__init__,将self.logger覆盖为Parent类的logger,再次调用self.Print(),打印的是Parent的logger地址。
    • 最后执行Child.__init__,将self.logger覆盖为Child类的logger,调用self.Print()后打印的是Child的logger地址。
  • 你看到的“每个继承层级logger地址不同”,本质是每个父类初始化时先设置了自己的logger,之后子类才覆盖它,而Print方法的调用时机刚好在每个父类初始化完成、子类还未覆盖logger之前。

修复方案

下面提供两种常用的修复思路,根据你的需求选择:

方案1:将logger定义为类属性(推荐)

把logger设置为类级别的属性,子类直接覆盖父类的类属性,这样实例会优先使用子类的logger,无需在__init__中重复赋值:

import logging
import sys

logFormat = '%(asctime)s.%(msecs)03d %(levelname)-8s [%(filename)s:%(funcName)s] %(message)s'
dateFormat = '%Y-%m-%d %H:%M:%S'
logFormatter = logging.Formatter(fmt=logFormat, datefmt=dateFormat)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logFormatter)
logging.getLogger().addHandler(handler)

class Person:
    logger = logging.getLogger('Person')
    def __init__(self):
        self.Person = 'Person'
        self.Print()

    def Print(self):
        self.logger.debug('Hello From Person')
        self.logger.debug('{}'.format(self.__dict__))

class Parent(Person):
    logger = logging.getLogger('Parent')
    def __init__(self):
        super().__init__()
        self.Parent = 'Parent'
        self.Print()
    def Print(self):
        self.logger.debug('Hello from Parent')
        self.logger.debug('{}'.format(self.__dict__))

class Child(Parent):
    logger = logging.getLogger('Child')
    def __init__(self):
        super().__init__()
        self.Child = 'Child'
        self.Print()
    def Print(self):
        self.logger.debug('Hello from Child')
        self.logger.debug('{}'.format(self.__dict__))

root = logging.getLogger()
root.setLevel(logging.DEBUG)

p = Person()
print('Person Class Created.')

c = Child()
print('Child Class Created.')

此时创建Child实例时,所有Print调用都会使用Child类的logger,因为实例的self.logger会优先读取子类的类属性。

方案2:通过重写方法动态获取logger

在父类中定义一个获取logger的方法,子类重写该方法,确保初始化时直接获取子类的logger,避免后续覆盖:

import logging
import sys

logFormat = '%(asctime)s.%(msecs)03d %(levelname)-8s [%(filename)s:%(funcName)s] %(message)s'
dateFormat = '%Y-%m-%d %H:%M:%S'
logFormatter = logging.Formatter(fmt=logFormat, datefmt=dateFormat)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logFormatter)
logging.getLogger().addHandler(handler)

class Person:
    def __init__(self):
        self.logger = self.get_logger()
        self.Person = 'Person'
        self.Print()

    def get_logger(self):
        return logging.getLogger('Person')

    def Print(self):
        self.logger.debug('Hello From Person')
        self.logger.debug('{}'.format(self.__dict__))

class Parent(Person):
    def get_logger(self):
        return logging.getLogger('Parent')
    
    def __init__(self):
        super().__init__()
        self.Parent = 'Parent'
        self.Print()
    def Print(self):
        self.logger.debug('Hello from Parent')
        self.logger.debug('{}'.format(self.__dict__))

class Child(Parent):
    def get_logger(self):
        return logging.getLogger('Child')
    
    def __init__(self):
        super().__init__()
        self.Child = 'Child'
        self.Print()
    def Print(self):
        self.logger.debug('Hello from Child')
        self.logger.debug('{}'.format(self.__dict__))

root = logging.getLogger()
root.setLevel(logging.DEBUG)

p = Person()
print('Person Class Created.')

c = Child()
print('Child Class Created.')

这种方式利用多态特性,父类初始化时调用的get_logger()实际是子类的实现,所以self.logger从一开始就是子类的logger,后续父类的初始化不会覆盖它。

内容的提问来源于stack exchange,提问作者Daniel Portugal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:17:53