1

Here I just copy my python code from my project:

from numpy.core.function_base import linspace
import numpy as np
from operator import mod



class contactPair:
    itr = 0
    rawData=[]
    rawDataLabel=[]

    uidi=0
    uidj=0
    Nc=0
    cLoc=[0,0,0]
    deltan=0
    deltatAcc=0
    Np=0
    Fi=[0,0,0]
    Fni=[0,0,0]
    Fti=[0,0,0]
    n=[0,0,0]
    t=[0,0,0]
    RVCP=[0,0,0]

    def __init__(self,itr,label,cntinfo):
        self.itr=itr
        self.rawData=cntinfo
        self.rawDataLabel=label
        self.uidi=cntinfo[0]
        self.uidj=cntinfo[1]
        self.Nc=cntinfo[2]
        self.deltan=cntinfo[6]
        self.deltatAcc=cntinfo[7]
        self.Np=cntinfo[8]
        for k in range(3):
            self.cLoc[k]=cntinfo[3+k]
            self.Fi[k]=cntinfo[9+k]
            self.Fni[k]=cntinfo[12+k]
            self.Fti[k]=cntinfo[15+k]
            self.n[k]=cntinfo[18+k]
            self.t[k]=cntinfo[21+k]
            self.RVCP[k]=cntinfo[24+k]
        g=1


class par:
    name = 0
    itr = 0
    uid = 0
    rawData = []
    rawDataLabel = []

    mass = 0
    pos = [0,0,0]
    vel = [0,0,0]
    acc = [0,0,0]
    acc2 = [0,0,0]
    hf =[0,0,0]
    theta=[0,0,0]
    omega=[0,0,0]
    alpha=[0,0,0]
    alpha2=[0,0,0]
    dia=[0,0,0]
    Nc=0
    ppf=[0,0,0]
    rotMat=[]
    vol=0
    density=0
    mofi=[]
    normalUnit=[]

    angleToShearingPlane=0
    def __init__(self,itr,label,parinfo):
        #self.name=0
        self.itr=itr
        self.rawData=parinfo
        self.rawDataLabel=label
        self.name=parinfo[0]
        self.mass=parinfo[2]
        for k in range(3):
            self.pos[k]= parinfo[3+k]
            self.vel[k]= parinfo[6+k]
            self.acc[k]= parinfo[9+k]
            self.acc2[k]= parinfo[12+k]
            self.hf[k]= parinfo[15+k]
            self.theta[k]=parinfo[18+k]
            self.omega[k]=parinfo[21+k]
            self.alpha[k]=parinfo[24+k]
            self.alpha2[k]=parinfo[27+k]
            self.dia[k]= parinfo[33+k]
            self.ppf[k]= parinfo[39+k]
        self.uid =  parinfo[36]
        self.Nc =  parinfo[37]
        
        #ipDir1,ipDir2,ipDir3,mat=rotate(self.theta[0],self.theta[1],self.theta[2],1,0,0) 
        #self.rotMat=mat
        #self.normalUnit=[ipDir1,ipDir2,ipDir3]
        self.angleToShearingPlane=mod(self.theta[2]+2*np.pi,np.pi)
        #self.angleToShearingPlane=mod(self.theta[1]+2*np.pi,np.pi)
        


t1=linspace(1,100,100)
c1=contactPair(1,[],t1)
p1=par(1,[],t1)

print(f"c1.cLoc: {c1.cLoc}")
print(f"p1.acc: {p1.acc}")

t2=linspace(101,200,100)
c2=contactPair(2,[],t2)
p2=par(1,[],t2)

print(f"c2.cLoc: {c2.cLoc}")
print(f"p2.acc: {p2.acc}")
print(f"c1.cLoc: {c1.cLoc}")
print(f"p1.acc: {p1.acc}")

and what I got from the python console is

c1.cLoc: [4.0, 5.0, 6.0]
p1.acc: [10.0, 11.0, 12.0]
c2.cLoc: [104.0, 105.0, 106.0]
p2.acc: [110.0, 111.0, 112.0]
c1.cLoc: [104.0, 105.0, 106.0]
p1.acc: [110.0, 111.0, 112.0]

What I did is

  1. defining two classes, i.e., contactPair and par.
  2. creating two new instances c1, p1 based on input t1.
  3. creating another two new instances c2, p2 based on input t2.

I don't know why the values of c1 and p1 are changed after creating c2 and p2.

3
  • Might be a duplicate of [this] (stackoverflow.com/questions/11217458/…). Commented Jan 20, 2021 at 6:54
  • @MarcoMaduri it's a little bit different. In this post actually instances of the class are being instantiated, and changes are being made to mutable class variables. In your referenced post, no instantiation happens. That person is creating aliases to the same class itself and is changing immutable class variables. Commented Jan 20, 2021 at 7:32
  • Please read about how to provide a minimal reproducible example, emphasis on the minimal part Commented Jan 20, 2021 at 7:45

1 Answer 1

1

It's because you define everything as class variables instead of instance variables.

Focusing on cLoc inside of contactPair for example - you define it is a class variable that belongs to the entire class contactPair instead of belonging to an instance. This means that every instance of this class actually shares the same cLoc value. You don't want this to be the case, so you should instead have each instance create it's own version of the cLoc variable inside the __init__ constructor and assign it as an attribute of self.

Let me do a simple example illustrating this.

>>> class A():        
...     class_var = []                                  
...     def __init__(self):                             
...         self.instance_var = []                      
...     def append_more(self,x):                        
...         self.class_var.append(x)                    
...         self.instance_var.append(x)                 
...                                                     
>>> a = A()                                             
>>> b = A()                                             
>>> a.append_more(1)                                    
>>> b.append_more(2)                                    
>>> a.instance_var                                      
[1]                                                     
>>> b.instance_var                                      
[2]                                                     
>>> a.class_var                                         
[1, 2]                                                  
>>> b.class_var                                         
[1, 2]                                                  
>>> A.class_var                                         
[1, 2]                                                  
>>> A.instance_var                                      
Traceback (most recent call last):                        
File "<stdin>", line 1, in <module>                   
AttributeError: type object 'A' has no attribute 'instance_var'                                                 >>>

The class_var variable actually belongs to the class A, not to instances a or b. Executing a.class_var.append(x) basically results in python doing the following:

  • check if "class_var" exists in the object namespace for a - it does not
  • check if "class_var" exists in the object namespace for a.__class__ (which is A) - it does
  • take the a.__class__.class_var that was found and call .append(x) on it

This basically means the expressions a.class_var or b.class_var refer to the same object as A.class_var.

But there was no instance_var variable created in the class context for A. That gets created in the __init__ constructor for specific instances and gets assigned to the instance not the class. This means that each instance has a unique list bound to instance_var in their object namespace.

Referring to a.instance_var causes python to find something at the instance level and not go and check the class level.

Note that if you were to overwrite class_var for a specific instance, you would basically cause the instance to point to some other object besides the class variable. This would not affect the original class variable or any other instance that did not do a similar "overwrite". For example, continuing the code above:

>>> a.class_var = [] # this makes a class_var reference to a new unrelated list at the instance level in a                              
>>> a.class_var                                         
[]                                                      
>>> b.class_var                                         
[1, 2]                                                  
>>> A.class_var                                         
[1, 2]                                                  
>>> a.append_more(3) # will use new fresh unrelated list                                   
>>> b.append_more(4) # will use original list belonging to class                                  
>>> a.class_var                                         
[3]                                                     
>>> b.class_var                                         
[1, 2, 4]                                               
>>> A.class_var                                         
[1, 2, 4]                                               
>>>
Sign up to request clarification or add additional context in comments.

3 Comments

class A(): class_var = [] float_var = 0 def __init__(self): self.class_var = [] self.instance_var = [] def append_more(self,x): self.class_var.append(x) self.instance_var.append(x) self.float_var=x a = A() b = A() a.append_more(1) b.append_more(2) What I got is a.float_var=1, b.float_var=2, A.float_var=0
That makes sense. Unlike lists, floating point values are immutable and cannot be changed. When you call append_more you add an instance reference to a new object within the instance namespace which does not affect the original class variable.
@guojuw in general, you can call id() on a variable to see a unique ID for the object in memory. If two variable names have the same ID, then they refer to the same underlying object

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.