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
相关产品推荐
相关产品推荐

