-1

I create a class with a dictionary variable, and a method for editing it:

class Test:
   TestDict = {"TestKey": "OldValue"}

   def ChangeDictionary(self):
       self.TestDict["TestKey"] = "NewValue"

Then, i create a two objects:

ObjectOne = Test()
ObjectTwo = Test()

Print their dictionaries before ChangeDictionary method call, and after:

# Print values before method call
print(ObjectOne.TestDict)
print(ObjectTwo.TestDict)

# Call method
ObjectTwo.ChangeDictionary()

# Print values after method call
print(ObjectOne.TestDict)
print(ObjectTwo.TestDict)

And i get this output:

{'TestKey': 'OldValue'}
{'TestKey': 'OldValue'}
    * * Method call * *
{'TestKey': 'NewValue'}
{'TestKey': 'NewValue'}

So method ChangeDictionary which was called from second object changed value also and in first object.
Is it Python language bug or reasons of it somewhere else?

Updated: If class variable will be any other type (For example - String) - In first object variable will have old value:

class Test:
    TestString = "OldValue"

    def ChangeString(self):
        self.TestString = "OldValue"
Komp Tip
  • 467
  • 5
  • 8
  • @tevemadar If i get it right - No. In topic which link you posted talks about changing variables of class from object. My question is why dictionary value of object changing after i call a method from other object (Instanse of same class), if i will change variable type to any other (For example string) values will be stay different. – Komp Tip Aug 15 '21 at 01:06
  • `TestDict` is shared across all objects since its not defined inside `__init__`. To fix this, create a `self.TestDict` and initialize it inside the `__init__` function. Check my answer for details. – Akshay Sehgal Aug 15 '21 at 01:09
  • Do mark the answer if it helped solve your question :) – Akshay Sehgal Aug 15 '21 at 01:13
  • @AkshaySehgal But why its all works only with dictionary? If variable will have string type, after i change it - First object will have old value. – Komp Tip Aug 15 '21 at 01:14
  • it works for all types of variables. please update the question (add the updated class) with the case that you are mentioning. – Akshay Sehgal Aug 15 '21 at 01:15
  • @AkshaySehgal question updated. – Komp Tip Aug 15 '21 at 01:22
  • 1
    "Is it Python language bug" this should never be your first inclination for something like this, almost certainly, you are just not understanding something – juanpa.arrivillaga Aug 15 '21 at 01:36

1 Answers1

1

As defined in this top answer:

  • Elements outside the init method are static elements; they belong to the class.
  • Elements inside the init method are elements of the object (self); they don't belong to the class.

To fix this behavior, try this -

class Test:
    def __init__(self):                          #<-------
        self.TestDict = {"TestKey": "OldValue"}  #<-------
    
    def ChangeDictionary(self):
        self.TestDict["TestKey"] = "NewValue"
        
        
ObjectOne = Test()
ObjectTwo = Test()

# Print values before method call
print(ObjectOne.TestDict)
print(ObjectTwo.TestDict)

# Call method
ObjectTwo.ChangeDictionary()
print("** Method call **")

# Print values after method call
print(ObjectOne.TestDict)
print(ObjectTwo.TestDict)
{'TestKey': 'OldValue'}
{'TestKey': 'OldValue'}
** Method call **
{'TestKey': 'OldValue'}
{'TestKey': 'NewValue'}

EDIT: As mentioned by @juanpa.arrivillaga, modifications you are making with your class function are defining an immutable object separate from the one that you have before.

Note the different in the 2 methods below -

#### ORIGINAL STRING ####

class Test:
    TestString = "OldValue"

    def ChangeString(self):
        self.TestString = "NewValue"
        
        
ObjectOne = Test()
ObjectTwo = Test()

# Print values before method call
print(ObjectOne.TestString)
print(ObjectTwo.TestString)

# Call method
ObjectTwo.ChangeString()
print("** Method call **")

# Print values after method call
print(ObjectOne.TestString)
print(ObjectTwo.TestString)

print("** Self dicts **")
print(ObjectOne.__dict__)
print(ObjectTwo.__dict__)
OldValue
OldValue

** Method call **
OldValue
NewValue

** Self dicts **
{}
{'TestString': 'NewValue'}

AND, with __init__


### FIXED STRING ###

class Test:
    def __init__(self):
        self.TestString = 'OldValue'
    
    def ChangeString(self):
        self.TestString = "NewValue"
               
ObjectOne = Test()
ObjectTwo = Test()

# Print values before method call
print(ObjectOne.TestString)
print(ObjectTwo.TestString)

# Call method
ObjectTwo.ChangeString()
print("** Method call **")

# Print values after method call
print(ObjectOne.TestString)
print(ObjectTwo.TestString)

print("** Self dicts **")
print(ObjectOne.__dict__)
print(ObjectTwo.__dict__)
OldValue
OldValue
** Method call **
OldValue
NewValue
** Self dicts **
{'TestString': 'OldValue'}
{'TestString': 'NewValue'}
Akshay Sehgal
  • 18,741
  • 3
  • 21
  • 51
  • Thanks, but why is it works only with dictionary type variables? If variable will be a string - Even if they will define ouside __init__ - They will changed only in their object. – Komp Tip Aug 15 '21 at 01:35
  • "Variable set outside `__init__` belong to the class." Not exactly. Variables set in the class block *belong to the class*. Instance variables are any attribute you assign to an instance. – juanpa.arrivillaga Aug 15 '21 at 01:36
  • 1
    @KompTip no, no they won't. `str` objects *are immutable* so you cannot mutate them. You are probably *assigning another string* inside your method, which shadows your class variable – juanpa.arrivillaga Aug 15 '21 at 01:37
  • @juanpa.arrivillaga, updated language and added reference. – Akshay Sehgal Aug 15 '21 at 01:38
  • @juanpa.arrivillaga Thanks, so when we edit defiened out of ```__init__``` list/dictionary variable - We are change value of class variable, when we change a string variable value - We redefine/recreate it? – Komp Tip Aug 15 '21 at 01:47
  • 1
    @KompTip no, the *type of the object is irrelevant*. If you assign a variable to `self` inside a method, i.e. the value of the first positional argument, that is assigning the attribute *to the instance*. Consider something like `class Foo: pass`, then if you do `foo = Foo(); foo.bar = 42` then you *created an instance variable*. That is **the exact same thing** that happens in a method, `def some_method(self): self.bar = 42`, `self` is just a naming convention, when you call `some_method` on an instance, the instance itself is passed as the first argument\ – juanpa.arrivillaga Aug 15 '21 at 01:50
  • @juanpa.arrivillaga Alright, big thanks. – Komp Tip Aug 15 '21 at 02:05