Paul
Paul

Reputation: 75

Python class member changes value without assignment

I have a class "test" of which I declare two instances, and on which I call an "update" function. "test.a" is a numpy array.

b1 = test()
b1.randomize(2, 0, 0, 0, 0, 0)
b2 = test()
b2.randomize(6, 0, 0.5, 0, 0, 0)
print(b1.a)
print(b2.a)
update(b1,b2)
print(b1.a)
print(b2.a)

The update function is defined as below. The issue is that the value of b2.a changes after the call of update, despite never assigning it therein. I also never made an assignment like "b2.a = b1.a", which would change the reference of b2.a. The class definition is also copied in below.

def update(b1, b2):
  dx = b1.p[0,0] - b2.p[0,0]
  dy = b1.p[0,1] - b2.p[0,1]
  b1.a[0,0] += (-dx) * b2.m
  b1.a[0,1] += (-dy) * b2.m

class test:
  m = 1.0
  r = 0.1
  p = np.zeros((1,2))
  v = np.zeros((1,2))
  a = np.zeros((1,2))

  def randomize(self, m_mean, m_stddev, pos_mean, pos_stddev, v_mean, v_stddev):
      self.m = np.random.normal(loc = m_mean, scale = m_stddev)
      self.p = np.random.normal(loc = pos_mean, scale = pos_stddev, size = (1,2))
      self.v = np.random.normal(loc = v_mean, scale = v_stddev, size = (1,2))

Upvotes: 1

Views: 569

Answers (4)

Amit Vikram Singh
Amit Vikram Singh

Reputation: 2128

Problem is here is that a is a class variable and hence is shared between both b1 and b2.

Though this would have not been an issue if you were assigning a new object to b1.a like b1.a = np.ones((1, 2)) because this will declare a new instance variable a for instance b1 and while b2.a still refers to the class object. So b1.a will change but b2.a will not. To be precise, b1.a and b2.a are now reference to two different objects.

>>> b1, b2 = test(), test()
>>> print("b1.a = {}, b2.a = {}".format(b1.a, b2.a)) 
b1.a = [[0. 0.]], b2.a = [[0. 0.]]

>>> b1.a = np.ones((1, 2))
>>> print("b1.a = {}, b2.a = {}".format(b1.a, b2.a)) 
b1.a = [[1. 1.]], b2.a = [[0. 0.]]

But when you do b1.a[0, 0] = (-dx) * b2.m you are changing the value inside the container to which a is an reference. Since this container is shared by both b1.a and b2.a, in this case both b1.a and b2.a are changed as they are still pointing to the same class object(or the container).

>>> b1, b2 = test(), test()
>>>> print("b1.a = {}, b2.a = {}".format(b1.a, b2.a))
b1.a = [[0. 0.]], b2.a = [[0. 0.]]

>>> b1.a[0, 0] = 100  # 
>>> print("b1.a = {}, b2.a = {}".format(b1.a, b2.a)) 
b1.a = [[100.   0.]], b2.a = [[100.   0.]]

Upvotes: 1

GeorgesAA
GeorgesAA

Reputation: 153

I see a couple of issues.

  • Missing init
  • Not using self to initialize (n, m, p, r, v, a)

Expanding on the above, you also should avoid accessing class variable directly. Gave you a small example of a getter and setter functions that you can use to access the private variables of a class.

class test:
    @property
    def m(self):
        return self._m
    @m.setter
    def m(self, value):
        self._m = value

    def __init__(self):
       self._m = 1.0
       self._r = 0.1
       self._p = np.zeros((1,2))
       self._v = np.zeros((1,2))
       self._a = np.zeros((1,2))

  def randomize(self, m_mean, m_stddev, pos_mean, pos_stddev, v_mean, v_stddev):
      self._m = np.random.normal(loc = m_mean, scale = m_stddev)
      self._p = np.random.normal(loc = pos_mean, scale = pos_stddev, size = (1,2))
      self._v = np.random.normal(loc = v_mean, scale = v_stddev, size = (1,2))

Upvotes: 1

piertoni
piertoni

Reputation: 2021

You are using improperly attributes of the instance you are creating. When you define in python just after the class a variable this will be common between different instances of the same object and so if one of the instances changes value of the attribute this will also be changed for other instances.

What you need to do is to define values when you instantiate the object, this is done conventionally inside the __init__ method like the this:

class Test:
    def __init__(self):
        self.m = 1.0
        self.r = 0.1

Check for example https://realpython.com/lessons/class-and-instance-attributes/ for more information.

Upvotes: 1

joao
joao

Reputation: 2293

a is a class variable, so it's shared by all the instances. If you want an a variable that's unique to each instance, you need to add the following in your class:

def __init__(self):
    self.a = np.zeros((1,2))

Upvotes: 1

Related Questions