Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing field: different value if its stored in a dict

My problem looks something like this.

# Module.py
class TestClass:
    name = "default"
    nameDict = {'name':"standard"}

    def __init__(self, name):
       self.name = name
       self.nameDict['name'] = name

Then I call on the fields from another module. The class is initiated before and I call on the name field like this:

Module.TestClass("newName")

# Inside another function
print(Module.TestClass.name)
print(Module.TestClass.nameDict['name'])

Which gives:

default #name
newName #nameDict

I don't understand why it would be different values. Does anyone have any idea why this would happen?

Thanks for your time.

like image 738
Smrow Avatar asked Aug 10 '15 08:08

Smrow


People also ask

How can you access a value stored in a dictionary?

There are two ways to access a single value of a dictionary: The square brackets [] approach. The safer get() method.

Should I use dict () or {}?

With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.


2 Answers

This is an interesting question; there's a lot going on.

Module.TestClass is the class you've defined in 'Module.py'. With Module.TestClass("newName"), you're creating an instance of that class, but you're not saving it.

When you do print(Module.TestClass.name), you're printing the value of the name attribute of Module.TestClass, which is the class, not an instance of the class. The name you gave the instance before doesn't change the class' name attribute.

The really interesting bit is print(Module.TestClass.nameDict['name']). Even though you're still accessing the nameDict attribute of the class, it is affected by the instance you created earlier. This is because Python variables hold references to objects. When you defined the class, you assigned a dictionary to the attribute nameDict in the class scope. That exact same dictionary object is being pointed at by the class and all the instances of the class. So, if you change it in one place, it's changed everywhere, because it's actually the exact same dict.

Here's a good way to do what it looks like you're trying to do:

# Module.py

class TestClass(object):
    def __init__(self, name='default'):
        self.name = name
        self.nameDict = {'name': name}

# other.py

testclass_instance = Module.TestClass("newName")

# Inside another function
print(testclass_instance.name)  # newName
print(testclass_instance.nameDict['name'])  # newName
like image 67
Cyphase Avatar answered Nov 14 '22 22:11

Cyphase


The difference you are seeing is because -

  1. Strings are immutable , whereas dictionaries are mutable .

  2. In your init function , when you do - self.name = name - this creates assigns a new reference to name in your instance , that does not change the reference that the class variable name holds .

  3. In your init function , when you change the value for the name key in the dictionary , you are mutating the dictionary (which is the same dictionary for the class variable) , hence it reflects in the class variable dictionary as well. If you had assigned a new dictionary here , you would be able to see the old value in the class variable still.

This basically happens because when you create an instance , the class variables' reference are copied as such to the instance, the reference still pointing to whatever class variable pointed to at that time. Since in this case, as the reference is same the changes to dict reflect in the class variable.

But when you do assignment, you are making the variable point to a new reference, and this does not cause the class variable to change.

A simple example to show this -

>>> class CA:
...     d = [1,2]
...
>>> CA.d
[1, 2]
>>> c = CA()
>>> c.d
[1, 2]
>>> id(c.d) == id(CA.d)
True
>>> c.d.append(3)
>>> CA.d
[1, 2, 3]
>>> c.d = [1,2,3,4,5]
>>> CA.d
[1, 2, 3]
>>> CA.d = [1,2,3,4,5,6]
>>> CA.d
[1, 2, 3, 4, 5, 6]
>>> d = CA()
>>> d.d
[1, 2, 3, 4, 5, 6]
>>> c.d
[1, 2, 3, 4, 5]
like image 29
Anand S Kumar Avatar answered Nov 14 '22 23:11

Anand S Kumar