4

So Python isn't my strong suit and I've encountered what I view to be a strange issue. I've narrowed the problem down to a few lines of code, simplifying it to make asking this question easier. I have a list of objects, this object:

class FinalRecord():
     ruid = 0
     drugs = {}

I create them in the shell like this:

finalRecords = []
fr = FinalRecord()
fr.ruid = 7
finalRecords.append(fr)
fr2 = FinalRecord()
fr2.ruid = 10
finalRecords.append(fr2)

As soon as I want to change the drugs dict on one object, it changes it for the other one too

finalRecords[0].drugs["Avonex"] = "Found"

I print out this:

finalRecords[1].drugs

and it shows:

{'Avonex':'Found'}

When I'm expecting it to actually be empty. I know I'm not completely understand how Python is working with the objects, can anyone help me out here?

Spencer Sutton
  • 2,907
  • 2
  • 18
  • 19

2 Answers2

5

The reason for this is because drugs is a class attribute. So if you change it for one object it will in fact change in others.

If you are looking to not have this behaviour, then you are looking for instance attributes. Set drugs in your __init__ like this:

class FinalRecord():
    def __init__(self):
        self.ruid = 0
        self.drugs = {}

Take note of the use of self, which is a reference to your object.

Here is some info on class vs instance attributes

So, full demo illustrating this behaviour:

>>> class FinalRecord():
...     def __init__(self):
...         self.ruid = 0
...         self.drugs = {}
...
>>> obj1 = FinalRecord()
>>> obj2 = FinalRecord()
>>> obj1.drugs['stuff'] = 2
>>> print(obj1.drugs)
{'stuff': 2}
>>> print(obj2.drugs)
{}
idjaw
  • 25,487
  • 7
  • 64
  • 83
  • 1
    Ah, I got ya, I didn't quite understand how classes worked in Python – Spencer Sutton Feb 19 '16 at 18:25
  • 1
    Basically methods (the def keyword) and variables declared at the class scope (indended one level up from the class keyword) are defining elements in a dictionary, that dictionary is your class. Some of the elements are functions (def) and others are class fields. Once you get the Dynamic nature of Python, a lot of things will make sense all at once. – Warren P Feb 19 '16 at 18:31
  • 1
    @SpencerSutton: You should consider accepting this answer if it actually helped you solve your problem. – code_dredd Feb 19 '16 at 18:31
  • it might have been six years, but you, Sir, just rescued me from a two day nightmare with that simple, but important answer – Confused Merlin Jul 29 '22 at 06:50
1

You define drugs as a class attribute, not an instance attribute. Because of that, you are always modifying the same object. You should instead define drugs in the __init__ method. I would also suggest using ruid as an argument:

class FinalRecord():
    def __init__(self, ruid):
        self.ruid = ruid
        self.drugs = {}

It could then be used as this:

fr = FinalRecord(7)
finalRecords.append(fr)
fr2 = FinalRecord(10)
finalRecords.append(fr2)

Or more simply:

finalRecords.append(FinalRecord(7))
finalRecords.append(FinalRecord(10))
zondo
  • 19,901
  • 8
  • 44
  • 83