0

I have created a unit class to manage units of physical quantities the user will input with their corresponding numerical values. The unit class takes a string input, and saves data in the form of three arrays containing each the physical units, their powers and a correspondence list that matches the kth element in that list to the correspondence[k]th element in the units array. In other words, the kth element represents the kth power in the powers list and the correspondence[k] element is its matching unit. Here is the definition of that class :

class unit:
    units = []
    powers = []
    correspondance = []
    def __init__(self,input_string):
        try:
            assert(type(input_string)==str)
        except AssertionError:
            print(type(input_string))
        new_string = True
        new_number = True
        for element in input_string :
            if element.isdigit() or element == "-":
                new_string = True
                if new_number:
                    self.powers.append("")
                    new_number = False
                    self.correspondance.append(len(self.units)-1)
                self.powers[-1] += element
            else:
                new_number = True
                if new_string :
                    self.units.append("")
                    new_string = False
                self.units[-1] += element

What happens is that when I define two objects p and q . q seems to have an odd behavior. Despite having the same definition as p :

p = unit("T")
print(p.units)
q = unit("T")
print(q.units)

Output

['T']
['T', 'T']

What is even strange is that when I change the order of print() functions I get yet another different output for the following code:

p = unit("T")
q = unit("T")
print(q.units)
print(p.units)

Output

['T', 'T']
['T', 'T']
MyDoom
  • 37
  • 1
  • 6

3 Answers3

2

The variables units, powers and correspondance of your class are class varibales, meaning that they belong to the class. This means that if you make an instance of the class, it does not have its own copy of these list. Instead, there is only exactly one copy which belongs to the class iself and it shared between all instances of it. So if you change it in the __init__ method, it will change for every instance of the class.

To fix this, you need to simply convert those class variables to instance varibles by initializing them inside the __init__ method:

    def __init__(self, input_string):
        self.units = []
        self.powers = []
        self.correspondance = []

Please use class variables only for constants that do not change.

Baumkuchen
  • 145
  • 8
1

The variables units, powers and correspondance are class attributes. In simple terms this means that all/any instances of the unit class will share these variables. You probably want instance variables instead. You can construct those during __init__

DarkKnight
  • 19,739
  • 3
  • 6
  • 22
1

Let me explain the part that is confusing you, but be aware that the code needs correction in the other parts.

class unit:
    units = []
    powers = []
    correspondance = []
    ...

units, powers and correspondence, the way you put it in the code, are class variables, that is: they will be shared by all objects of that class.

When running the code, the class is evaluated only once, checking if it is valid python code. At this moment, the class variables (units, powers and correspondence) are defined, with values:

units = []
powers = []
correspondance = []

Whenever you create an object:

p = unit("T")

or

q = unit("T"), 

the new method is called (constructor), and passes the object itself (self) to init (initializer)***. So each time an object is created, everything inside the init method will be called

So, when you create the object p, and run this line:

self.units[-1] += element,

the string T becomes part of the units class variable, being shared with all new objects that are created from there.

when you create the object q, passing the string T, it will again be called self.units[-1] += element, however, self.units already contains the element T, adding a new element in the last position (a new T).

So, if your goal is to reset these elements every time a new object is created, just pass units, power, correspondence into init

 def __init__(self, input_string):
     self.units = []
     self.powers = []
     self.correspondance = []

        ...

***NOTE: That's why the first parameter of every class initializer (init) is self

Magaren
  • 192
  • 8