Python Forum
Class Instances overriding class members and attributes.
Thread Rating:
  • 2 Vote(s) - 2.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Class Instances overriding class members and attributes.
#1
I'm trying to add each person to a list or dictionary. In this case it's a dictionary, but the same thing happens whether I reformat it to be a list that gets appended. I'm struggling to describe what is happening; other than I have a class with a list that whether I make it a instance member (self.lineage) or a class member as a static variable; the class only takes on the data of the instant. I thought perhaps that the methods variable 'l' was overriding the class method as it was originally called self.lineage.

When I add a for loop to show each iteration of what is going on it counts
0, 3, 3 with Person 2 and Person 3 retaining the values that Person 1 set. I'm not sure what's happening or what this is called, or why it's happening.

My goal is to add a person to a list, and then count the items in the list. I removed the original loop that did the counting since I realized my problem seems to be not understanding the anatomy of a class in python and how creating instances and working with variables and what gets overrided or not works.abs

Any help would be appreciated.

class Person():

    lineage = {}

    def __init__(self, data):
        self.data = data
        self.pid = 0
        # self.lineage = []

    def add_person(self, name):
        # renamed self.lineage to l to avoid instance member overriding class member
        l = Person(data).lineage
        self.name = name
        data["Name"] = self.name
        l.update(data) # prints the last instance
        # l = data # creates an empty list/dict
        print("Each single instance of Lineage")
        print(l)


data = {
    "Name": "Unknown",
    "PID": 00000,
    "Attributes": {
        "Gender": "Unknown",
        "Sex": "Unknown",
        "DOB": "Unknown"
    }
}

# I've tried making different instances ex. a = Person(data), z.add_person, but 
# it doesn't seem to solve the issue and need a single variable command anyway.
v = Person(data)
y = v.add_person("Person1") 
y = v.add_person("Person2") 
y = v.add_person("Person3")

print("Printing Total Lineage")
y = v.lineage
print(y)


"""
>>OUTPUT
Each single instance of Lineage
{'Name': 'Person1', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'DOB': 'Unknown'}}
Each single instance of Lineage
{'Name': 'Person2', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'DOB': 'Unknown'}}
Each single instance of Lineage
{'Name': 'Person3', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'DOB': 'Unknown'}}
Printing Total Lineage
{'Name': 'Person3', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'DOB': 'Unknown'}}
"""
Reply
#2
It sounds like you want lineage to be a list of dicts, instead of just a single dict that each call to add_person overwrites.
Reply
#3
Originally lineage was a list containing dicts but it still didn't solve the problem; so what I did was I changed it to dicts temporarily during my troubleshooting.I plan to change it back later, it's just that right now I'm stuck as to :

1. Understanding why it's doing that (terminology and how python processes wise)
2. Understanding what to do to actually solve the problem, as I've tried changing lineage from lists to dicts, to just another dict holding a list, to having list be a instance member, to having it be part of a separate class altogether that add_person calls into; no matter what I do it still keeps doing the same thing. either the list doesn't print out at all, or it prints out the last member. Something is overriding something and I'm not sure what, how or why. No matter what I've done so far I still can't fix it.

I've changed it back to list with dicts being added into the list, but still the same problem.
Reply
#4
My English is not perfect.

Do you want something like this?

In [1]: class Person:
    ...:     def __init__(self):
    ...:         self.data = {
    ...:     "Name": "Unknown",
    ...:     "PID": 00000,
    ...:     "Attributes": {
    ...:         "Gender": "Unknown",
    ...:         "Sex": "Unknown",
    ...:         "DOB": "Unknown"
    ...:         }
    ...:     }
    ...:     
    ...:     def add_person(self, name):
    ...:         self.data["Name"] = name
    ...:         return self.data
    ...:     

In [2]: persons = []

In [3]: for name in ["Tom", "John", "Donald"]:
    ...:     persons.append(Person().add_person(name))
    ...:     

In [4]: persons
Out[4]:
[{'Attributes': {'DOB': 'Unknown', 'Gender': 'Unknown', 'Sex': 'Unknown'},
  'Name': 'Tom',
  'PID': 0},
 {'Attributes': {'DOB': 'Unknown', 'Gender': 'Unknown', 'Sex': 'Unknown'},
  'Name': 'John',
  'PID': 0},
 {'Attributes': {'DOB': 'Unknown', 'Gender': 'Unknown', 'Sex': 'Unknown'},
  'Name': 'Donald',
  'PID': 0}]
"As they say in Mexico 'dosvidaniya'. That makes two vidaniyas."
https://freedns.afraid.org
Reply
#5
The reason why this happens:
When you create the person, you create a dictionary in the class. Lineage become a dictionary containing the key-value pairs defined in your 'data'.
When you add a person: you use the function update. If you have a closer look at the meaning of this function, it updates an existing dictionary with new key-value pairs. All existing key-value pairs are updated to the new value.

But I would not do this as you do: it's great you use a class, but a class should contain the information about a single item. Your class members should basically hold the values which you have defined in your data structure.
Then I would simply create a list, where each element is an item of the class 'person'.

Some other observations:
lineage is a class variable, so you should use it as
l = Person.lineage, not l = Person(data).lineage

In your function, you use data. The class however is initialized and the information is stored in self.data, not data.
You risk creating a local variable, which is different from self.data and which is gone when the add_person function completes.

Are you using a tool where you can step through your code? If not, I recommend PyCharm
Reply
#6
[UPDATE: SORRY I posted this before seeing the new comment/last reply I'm reading it now]


Thank you Wavic*. Class Person accepts "data" as an argument. And add_person accepts "name" as an argument, modifies the data and then is meant to copy over the new modified data to the list (name + attributes).When I try that I get a recursion error. When I try it outside the function it doesn't know what the variable data is, and then if I create the constant variable data, it gets stuck in the loop of me trying to append information to data and then nothing getting added to it (which always brings me back where I started).

"for name in data:
lineage.append(Person(data).add_person(name)) # Add Function"



class Person():

    lineage = []


    def __init__(self, data):
        self.data = data
        self.pid = 0
        # self.lineage = []

    def add_person(self, name):
        # renamed self.lineage to l to avoid instance member overriding class member
        l = Person(data).lineage
        self.name = name
        data["Name"] = self.name
        for name in data:
            l.append(Person(data).add_person(name)) 
        
        # l.update(data) # prints the last instance
        # l = data - creates an empty list/dict
        print("Each single instance of Lineage")
        print(l)
        print("Just ZERO")
        print(l[0])


data = {
    "Name": "Unknown",
    "PID": 00000,
    "Attributes": {
        "Gender": "Unknown",
        "Sex": "Unknown",
        "DOB": "Unknown"
    }
}

# I've tried making different instances ex. a = Person(data), z.add_person, but 
# it doesn't seem to solve the issue and need a single variable command anyway.
v = Person(data)
y = v.add_person("Person1") 
y = v.add_person("Person2") 
y = v.add_person("Person3")

print("Printing Total Lineage")
y = v.lineage
print(y)
print("JUST ZERO")
y = v.lineage[0]
print(y)

"""
>>OUTPUT
Traceback (most recent call last):
  File "help2.py", line 51, in <module>
    y = v.add_person("Person1")
  File "help2.py", line 28, in add_person
    l.append(Person(data).add_person(name))
  File "help2.py", line 28, in add_person
    l.append(Person(data).add_person(name))
  File "help2.py", line 28, in add_person
    l.append(Person(data).add_person(name))
  [Previous line repeated 991 more times]
  File "help2.py", line 24, in add_person
    l = Person(data).lineage
RecursionError: maximum recursion depth exceeded
"""


2.3 Update@BlueFlash
Quote:The reason why this happens:
When you create the person, you create a dictionary in the class. Lineage become a dictionary containing the key-value pairs defined in your 'data'.
When you add a person: you use the function update. If you have a closer look at the meaning of this function, it updates an existing dictionary with new key-value pairs. All existing key-value pairs are updated to the new value.

But I would not do this as you do: it's great you use a class, but a class should contain the information about a single item. Your class members should basically hold the values which you have defined in your data structure.
Then I would simply create a list, where each element is an item of the class 'person'.

Some other observations:
lineage is a class variable, so you should use it as
l = Person.lineage, not l = Person(data).lineage

In your function, you use data. The class however is initialized and the information is stored in self.data, not data.
You risk creating a local variable, which is different from self.data and which is gone when the add_person function completes.

Are you using a tool where you can step through your code? If not, I recommend PyCharm

Thank you for this insight BlueFlash; so a class should contain information about a single item?

When you say you would simply create a list, where each element is an item of the class 'person' -- would you recommend creating a new class entirely that holds the list of all persons? How would you recommend I send the data from one list to another class list without the problem that I seem to get stuck in where: the instance I've created, calls another class with the list of people. And then essentially does the same thing (creating a list and overriding a list).

The reason why it's stored in data is because the Person class inherits from another class called "Node" which contains information for traversing the data, backwards and forward. The help version I linked remove the inheritance in an attempt to problem solve. In the original one the person class looks like this:

class Person(Node):
	def __init__(self, data):
		Node.__init__(self, data) # Because self.data = data didn't seem to work. But using just "data" did
Currently I am using Visual Studio Code which I like a lot, but I've also tried Pycharm (which for some reason felt bulky)


3.4 Update @BlueFlash

I've been walking through your comment line by line. The two keys in your comment:
(1) A class should only hold information about a single instance
(2) Create a list outside the instance
(3) The instructions of add_function are telling it to overwrite/my instructions are flawed.

For #2 at first I thought you meant to create another class, and I was still stuck in that same class loop; but then I tried building a list outside of the class itself and seeing if it works. And it worked perfectly with the exception of I haven't yet figured out how to return the modified data back to the class; once I can figure out how to get that working I plan to put it into a class with a main function that I would run or something similar (though I'm not sure if that's the right way to think about it yet).

For #3 I feel a lot closer to solving the problem I have as before thanks to your help. One of the reasons could be the problem with self.data vs. data. One of the reasons why it's like that is because my Person class inherits from Node which has a "data" argument that allows me to link back and forth in the list. This Person class inherits from data. When I tried to use self.data I recall getting errors after inheriting data/overriding it, and was able to just use data without declaring it since the inheritance seemed to do so. (Not sure if that was the right way to go about it)



Class omitted
    def add_person(self, name):
        self.name = name
        data["Name"] = self.name
        print(data)
        return data

v = Person(data)
a = v.add_person("Person1")
b = v.add_person("Person2")
c = v.add_person("Person3")

lineage = [a, b, c]
pprint(lineage)

>>>Output
{'Name': 'Person1', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'Orientation': 'Unknown', 'DOB': 'Unknown'}}
{'Name': 'Person2', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'Orientation': 'Unknown', 'DOB': 'Unknown'}}
{'Name': 'Person3', 'PID': 0, 'Attributes': {'Gender': 'Unknown', 'Sex': 'Unknown', 'Orientation': 'Unknown', 'DOB': 'Unknown'}}
[{'Attributes': {'DOB': 'Unknown',
                 'Gender': 'Unknown',
                 'Orientation': 'Unknown',
                 'Sex': 'Unknown'},
  'Name': 'Person3',
  'PID': 0},
 {'Attributes': {'DOB': 'Unknown',
                 'Gender': 'Unknown',
                 'Orientation': 'Unknown',
                 'Sex': 'Unknown'},
  'Name': 'Person3',
  'PID': 0},
 {'Attributes': {'DOB': 'Unknown',
                 'Gender': 'Unknown',
                 'Orientation': 'Unknown',
                 'Sex': 'Unknown'},
  'Name': 'Person3',
  'PID': 0}]
Printing Total Lineage
[[[[I apologize for the multiple updates. I didn't refresh the page and saw a new post after I had posted; I was unable to delete the post to modify my response *after* experimenting myself with the concept, and so I quickly replied afraid the message would be seen and then ignored as it didn't include the second response, but messages I post afterwards are auto-added as updates. I'm now playing with this concept. This information you've given me thus far has been 3 major gems and clues that I think have pushed me in the right direction, both in greater understanding of what is going on and in an ability to do something with that understanding.]]]]
Reply
#7
example
class Attributes:
    def __init__(self, gender, sex, dob):
        self.gender = gender
        self.sex = sex
        self.dob = dob

    def __repr__(self):
        return "Attributes: {3}Gender: {0}, Sex: {1}, DOB: {2}{4}".format(self.gender, self.sex, self.dob, '{', '}')

class Person:
    def __init__(self, lineage, name, gender, sex, dob):
        self.name = name
        self.pid = lineage.pid
        lineage.pid += 1
        self.attributes = Attributes(gender, sex, dob)

    def dict(self):
        return {'name': self.name, 'pid': self.pid, 'attributes': vars(self.attributes)}

    def __repr__(self):
        return "Person: {3}Name: {0}, Pid: {1}, {2}{4}".format(self.name, self.pid, self.attributes, '{', '}')

class Lineage:
    def __init__(self, pid=1000):
        self.lineage = {}
        self.pid = pid

    def add_person(self, name, gender='Unknown', sex='Unknown', dob='Unknown'):
        self.lineage[name] = Person(self, name, gender, sex, dob)

    def values(self):
        return self.lineage.values()

    def __len__(self):
        return len(self.lineage)

    def __getitem__(self, key):
        return self.lineage[key]

    def __setitem__(self, key, value):
        self.lineage[key] = value

    def __str__(self):
        line = ""
        for person in self.lineage.values():
            line += repr(person) + '\n'
        return line

def main():
    lineage = Lineage()

    lineage.add_person("Person1")
    lineage.add_person("Person2")
    lineage.add_person("Person3")

    # if you want a dict
    print()
    # overload __getitem__
    print(lineage['Person1'].dict())
    print()

    print(lineage['Person2'])
    print()

    # overload __len__
    print("Count:", len(lineage))
    for person in lineage.values():
        print(person)
    # overload __str__
    print()
    print(lineage)

if __name__ == "__main__":
    main()
99 percent of computer problems exists between chair and keyboard.
Reply
#8
Thank you for your detailed example. My long-version of the code looks similar to this but I broke it down piece by piece to work on playing and exploring different aspects of Python. My goal is to learn to work with classes and to learn to get comfortable with passing mutable data to those classes and understanding exactly what is going on before refactoring the code to actually do things more efficiently. I want to understand what I'm doing and why things work the way they do.

Yesterday Blueflash offered me a major key (or keys):

(1) That when I create an instance I also create a dictionary at the same time in the class.
(2) When I call the add_person function it updates an existing dictionary with the key-values, and creates a new instance with each new one. # But esp. that my instructions in add_function and my lack of understanding of how python is interpreting the data is at work here.
(3) That a class should contain by design information about a single item.

I realized I was dealing with 2 separate problems. First I was dealing with the problem that data I sent through the function was sticky my llists would print out as 0 3 3 3 (this is when PID had a counter to it), and when I put an id on the object the first object was different and the 3 objects were different. And depending on how I did the code it sometimes printed out 3 3 3 3, the last instance created was the one witht he values. At the same time I was fighting with the data being created in the add_person but not being able to copy it to a new list anywhere because of 0 3 3 3 pattern; and because it would erase the data as soon as I created it. My goal was that before it erased the data to send it to a new global list that was shared between all instances or to pass it somewhere else within the same instance. And now that I understand more of what's going on under the hood I might be able to do that.

But what Blueflash told me yesterday gave me a hint to explore more and understand more.

So here is what I discovered.

I create an instance:
x = Person(data) <-- where this data is sort of template I want to modify.
then I used it to add_person.
a = x.add_person("Person1")
b = x.add_person("Person2"
c = x.add_person("Person3")

originally each value was given the same value but when I made the list outside of the add_person functions I needed to create list = [a, b, c] for it to work.

I tried to append it to a global list and even a class outside the list but had the same problem, (1) 0 3 3 3 (the data was sticking) or 3 3 3 3 (the last instance was being added three times to the list) and (2) that the list erased itself each time a new element was created.


the argument "data" was the cause for 0 3 3 3. If I changed data to, data2, or data3 the classes stopped trying to modify and override the original "data" and instead create new data.

This information allowed me to research in better terms what was happening. I remember saying it was as if Python remembers the values of the previous data, and it turns out python does.

It's the same problem I had when I first began working with importing in python and python functions. In that situation I couldn't understand how python import works and scope; in the function there was a similar problem. Once I learned how Python thinks and processes information that made sense. Classes are similar.

http://python.net/~goodger/projects/pyco...-variables

http://effbot.org/zone/default-values.htm

https://www.programiz.com/python-programming/closure

http://docs.python-guide.org/en/latest/writing/gotchas/

I was writing code and expecting it to do one thing and it did another:

(1) understanding how mutable objects vs. immutable objects react differently when passed as arguments.
(2) understanding how python binds variable data
(3) making sure I understand instance members vs. class members/attributes
(4) understanding closure in python

Even though I didn't have a nested function behaviour appears to be similar.

This allowed me to successfully achieve the goal I wanted and I was with few adjustments to this practice code, able to add the items one by one to the new list. With that knowledge I hope to attempt to write it more efficiently and will rewrite it a few times until it is more efficient.

I just wanted to share that as an update as I enjoyed the insight.

@Windspar

Thank you for your code example. I've saved it for reference for when I get to my next goal. Another reference I am also using is the family tree generator available on github. My goal is to learn from the code and then create something simple using similar concepts. But I had to break the problem down so I know the code I've shared here is not the ideal way to do things; but it's how I learn. Trying it one way, learning why it doesn't work well or efficiently, refactoring and refactoring with new experience gained each time.

My next goal is to build another class Lineages, and to explore what I have been calling callbacks and async coding but don't necessarily know if that's an accurate term. It's what I'm using to say when one function calls another function and another and they pass information back and forth to achieve the same thing. That's the ultimate outcome of what I'm attempting to code but have to work through it piece by piece to understand what I'm doing.

I'm not sure if what I've concluded or what I understand is accurate or not; so if I've made any mistakes in my understanding or if there are other insights on how python thinks and best practices or formulas (recipes) for achieving what I want, a pattern of coding I'll take it! :D
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  class and runtime akbarza 4 282 Mar-16-2024, 01:32 PM
Last Post: deanhystad
  Operation result class SirDonkey 6 422 Feb-25-2024, 10:53 AM
Last Post: Gribouillis
  The function of double underscore back and front in a class function name? Pedroski55 9 562 Feb-19-2024, 03:51 PM
Last Post: deanhystad
  super() and order of running method in class inheritance akbarza 7 594 Feb-04-2024, 09:35 AM
Last Post: Gribouillis
  Class test : good way to split methods into several files paul18fr 4 403 Jan-30-2024, 11:46 AM
Last Post: Pedroski55
  Good class design - with a Snake game as an example bear 1 1,713 Jan-24-2024, 08:36 AM
Last Post: annakenna
  question about __repr__ in a class akbarza 4 529 Jan-12-2024, 11:22 AM
Last Post: DeaD_EyE
  error in class: TypeError: 'str' object is not callable akbarza 2 445 Dec-30-2023, 04:35 PM
Last Post: deanhystad
  super() in class akbarza 1 402 Dec-19-2023, 12:55 PM
Last Post: menator01
  error occuring in definition a class akbarza 3 636 Nov-26-2023, 09:28 AM
Last Post: Yoriz

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020