CodeQL 文档

对象初始化期间多次调用 __init__

ID: py/multiple-calls-to-init
Kind: problem
Security severity: 
Severity: warning
Precision: very-high
Tags:
   - reliability
   - correctness
Query suites:
   - python-security-and-quality.qls

点击查看 CodeQL 代码库中的查询

与 Java 等静态类型语言不同,Python 允许在对象初始化期间调用方法时具有完全的自由度。但是,标准的面向对象原则适用于使用深度继承层次结构的 Python 类。因此,开发人员有责任确保在存在多个需要调用的 __init__ 方法时,对象被正确初始化。

在对象初始化期间多次调用 __init__ 方法会导致对象初始化错误。调用相关 __init__ 方法多次的可能性很小。

存在多种方式可以多次调用 __init__ 方法。

  • __init__ 方法的层次结构中可能存在多个对该方法的显式调用。

  • 使用多重继承的类直接调用其基类型的 __init__ 方法。其中一个或多个基类型使用 super() 传递继承链。

建议

要么注意不要显式调用 __init__ 方法多次,要么在整个继承层次结构中使用 super()

或者将一个或多个类重构为使用组合而不是继承。

示例

在第一个示例中,使用的是对 __init__ 的显式调用,但 SportsCar 错误地同时调用了 Vehicle.__init__Car.__init__。可以通过删除对 Vehicle.__init__ 的调用来修复此问题,如 FixedSportsCar 中所示。

#Calling a method multiple times by using explicit calls when a base inherits from other base
class Vehicle(object):
    
    def __init__(self):
        self.mobile = True
        
class Car(Vehicle):
    
    def __init__(self):
        Vehicle.__init__(self)
        self.car_init()
        
    def car_init(self):
        pass
    
class SportsCar(Car, Vehicle):
    
    # Vehicle.__init__ will get called twice
    def __init__(self):
        Vehicle.__init__(self)
        Car.__init__(self)
        self.sports_car_init()
        
    def sports_car_init(self):
        pass
        
#Fix SportsCar by only calling Car.__init__
class FixedSportsCar(Car, Vehicle):
    
    def __init__(self):
        Car.__init__(self)
        self.sports_car_init()
        
    def sports_car_init(self):
        pass
 

在第二个示例中,混合使用了对 __init__ 的显式调用和使用 super() 的调用。要修复此示例,应在整个继承层次结构中使用 super()


#Calling a method multiple times by using explicit calls when a base uses super()
class Vehicle(object):
     
    def __init__(self):
        super(Vehicle, self).__init__()
        self.mobile = True
        
class Car(Vehicle):
    
    def __init__(self):
        super(Car, self).__init__()
        self.car_init()
        
    def car_init(self):
        pass
        
class SportsCar(Car, Vehicle):
    
    # Vehicle.__init__ will get called twice
    def __init__(self):
        Vehicle.__init__(self)
        Car.__init__(self)
        self.sports_car_init()
        
    def sports_car_init(self):
        pass
        
#Fix SportsCar by using super()
class FixedSportsCar(Car, Vehicle):
    
    def __init__(self):
        super(SportsCar, self).__init__()
        self.sports_car_init()
        
    def sports_car_init(self):
        pass

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私