pythonpython-3.xforward-reference

about python recursive import in python3 when using type annotation


After spending another three hours, I thought I may got the solution.forward-references

if there isn't any better way to do this, I'll close this question later and post that solution

I'm doing reverse engineering on a program (and try to implement it using Python).

I got this problem

for example I have a class in ComponentOfCar.py, the code is as follows

__all__ = ("ComponentOfCar");
import Car;
#...#imports other needed packages

class ComponentOfCar(object):
    def __init__(self, owner:Car.Car):
        self.owner = owner;
        self.type = "Default";
        #....#create other attribute

Another class like this(Car.py):

__all__ = ("Car");
import ComponentOfCar;
#...#imports other needed packages

class Car(object):
    __slots__ = ("m_lstComponents",....);
    def __init__(self, data:FlatBuffersBinaryData):
        self.m_lstComponents = list();
        self.LoadFromData(data);
        #....#create other attribute
    def InstallComponen(self, component:ComponentOfCar.ComponentOfCar)->bool:
        self.m_lstComponents.append(component);
        return True;

But after doing so I came across this problem:

Traceback (most recent call last):
  File "Y:\Car.py", line 2, in <module>
    import ComponentOfCar;
  File "Y:\ComponentOfCar.py", line 2, in <module>
    import Car;
  File "Y:\Car.py", line 4, in <module>
    class Car(object):
  File "Y:\Car.py", line 10, in Car
    def InstallComponen(self, component:ComponentOfCar.ComponentOfCar)->bool:
AttributeError: module 'ComponentOfCar' has no attribute 'ComponentOfCar'

The original program is written in a compiled language. Inside that the class inheritance is very complicated, and it has hundreds classes, Which makes me headache.

I want to make this process a bit clearer by using type annotations and seperate each class into individual files. But this would have to use recursive import.

After googleing for half day, I havn't find some solution, so I come here to seek help. Is Python cannot do this like compiled language, or I just make some mistake? I'm confused. How can I fix it.

Sorry about my broken English. And thanks for your time. :)

to be detailed

Here is the structure of the decompiled class declaration like this(it's c#, the whole file is about a hundred thousands of lines):

// Namespace: GameNamespace
public class Car
{
    // Fields
    protected List`1<ComponentOfCar> m_lstComponents; 
    public int ObjectID; 

    // Methods
    public void .ctor(Data data);
    public void AddComponent(ComponentOfCar c);
}

// Namespace: GameNamespace
public abstract class ComponentOfCar
{
    // Fields
    protected Car m_owner;

    // Properties
    public Car Owner { get; }

    // Methods
    public Car get_Owner();
    public void .ctor(Car owner);
}

Or my question is how to use python to implement this clearly.

Yes, in my thought the way to do this showed above, I know that it's wrong, but I have no idea about how to make it right. I shouldn't separate them? Or I can have them written in another way (to avoid rescursive import and) to do the same thing as in c#?

Please tell me a way to solve this, thank you very much.

After spending another three hours, I thought I may got the solution

forward-references , I'm checking this. And if there isn't any better way to do this, I'll close this question later and post that solution, it may fixes my code.


Solution

  • Hope this would help,

    component.py

    class Component:
        def __init__(self, owner, data):
            self.owner = owner
            self.name = data['name']
    

    car.py

    from component import Component
    
    class Car:
        def __init__(self, data):
            self.components = []
            self.type = data['type']
            self.color = data['color']
    
        def add_car_components(self, data):
            self.components.append(Component(self, data));
    
    
    c = Car({'type': 'bus', 'color': 'red'})
    c.add_car_components({'name': 'frontdoor'})
    c.add_car_components({'name': 'reardoor'})
    
    for component in c.components:
        print(component.owner.type, component.owner.color, component.name)
    

    Result:

    ->python car.py
    
    bus red frontdoor
    bus red reardoor