Dependencies between attributes of 2 and more classes

Question:

Is it possible to initialize instances of classes with attributes that are dependent across (not in circular way)?

Today, I use dictionaries instead of classes as follows:

dictA = {'A1': 10}
dictB = {'B1': 2}
dictB['B2'] = dictA['A1'] * dictB['B1']
dictA['A2'] = dictB['B2'] * 3  # dictA['A1'] * dictB['B1'] * 3

print(dictA)  # {'A1': 10, 'A2': 60}
print(dictB)  # {'B1': 2, 'B2': 20}

When I try to express the above with classes, I end up with RecursionError: maximum recursion depth exceeded.

class A:
    def __init__(self):
        self.A1 = 10
        self.A2 = B().B2 * 3

class B:
    def __init__(self):
        self.B1 = 2
        self.B2 = self.B1 * A().A1
    
a = A()
b = B()

The dictionaries (classes) are separate because they represent different types of data (e.g. employees and time).

Asked By: aeiou

||

Answers:

You need to make the initializers non-circular. Then you can use @property to lazily evaluate the properties

class A:
    def __init__(self):
        self.A1 = 10

    @property
    def A2(self):
        return B().B2 * 3


class B:
    def __init__(self):
        self.B1 = 2

    @property
    def B2(self):
        return self.B1 * A().A1


a = A()
b = B()

print(a.A1, a.A2, b.B1, b.B2)

Result:

10 60 2 20
Answered By: rdas

Another alternative:

class A:
    def __init__(self):
        self.A1 = 10
    def add_A2(self, x):
        self.A2 = x.B2 * 3

class B:
    def __init__(self):
        self.B1 = 2
    def add_B2(self, x):
        self.B2 = self.B1 * x.A1
    
a = A()
b = B()
b.add_B2(a)
a.add_A2(b)
Answered By: Alex

You get a RecursionError because you create an instance of B to set A2, and to create an instance of B you need to create an instance of A to set B2. You will keep creating instances of A and B in a circular manner.

You can declare class attributes, which can be used without creating an instance of the class. Note that this value is always the same for all instances of a class. So you can use it for a value like days_of_week = 7, but not for a persons age age = 34, as a persons age is not always 34.

Assuming A1 and A2 can be class attributes, which are always the same, you could do the following:

class A:
    A1 = 10
    def __init__(self):
        self.A2 = B.B1 * self.A1 * 3
    
class B:
    B1 = 2
    def __init__(self):
        self.B2 = self.B1 * A.A1
        
a = A()
b = B()
print(a.A1, a.A2)
print(b.B1, b.B2)

Answered By: dkruit