pythonpycharmpython-typing

Pycharm static analysis doesn't infer subclass types as I wished


In the following python code, the TruckDriver class inherits from the Driver class, and the Truck class inherits from the Vehicle class.

The driver attribute of the Truck class is of TruckDriver type (which is specified in the type hint), and is passed to the constructor of the Vehicle class (which accepts the general Driver type).

However, Pycharm warns me in the Truck class about the driver not having TruckDriver-specific attributes. Specifically, it underlines the company attribute in the print(f"Company: {self.driver.company}") line, with the following warning :

Unresolved attribute reference 'company' for class 'Driver'

Is there a way I can resolve such warnings? I also use Ruff as a linter in my project but it doesn't seem to help here.

class Driver:
    def __init__(self, name: str):
        self.name = name

class TruckDriver(Driver):
    def __init__(self, name: str, company: str):
        super().__init__(name)
        self.company = company

class Vehicle:
    def __init__(self, driver: Driver):
        self.driver = driver

    def drive(self):
        print(f"{self.driver.name} is driving.")

class Truck(Vehicle):
    def __init__(self, driver: TruckDriver):
        super().__init__(driver)

    def drive(self):
        super().drive()
        print(f"Company: {self.driver.company}")

Solution

  • Use a generic vehicle:

    from typing import Generic, TypeVar
    
    D = TypeVar("D", bound="Driver")
    
    class Driver:
        def __init__(self, name: str):
            self.name = name
    
    class TruckDriver(Driver):
        def __init__(self, name: str, company: str):
            super().__init__(name)
            self.company = company
    
    class Vehicle(Generic[D]):
        def __init__(self, driver: D):
            self.driver = driver
    
        def drive(self):
            print(f"{self.driver.name} is driving.")
    
    class Truck(Vehicle[TruckDriver]):
        def __init__(self, driver: TruckDriver):
            super().__init__(driver)
    
        def drive(self):
            super().drive()
            print(f"Company: {self.driver.company}")
    
    alice = Driver("Alice")
    carol = TruckDriver("Carol", "Acme Truck Co.")
    
    car = Vehicle(alice)
    truck = Truck(carol)
    
    car.drive()
    truck.drive()