Struggling with the __del__ methode and append function within the same class

Hello folks; My name is Jürgen an I am new here.

I have a problem with playing with some class function although I have read some books and were watching some tutorials, however, my simple code is not working proberly.
I have greated a class where I want to collect each object in a list in order to print all objects if needed later on. On the other hand I created a del method to remove objects if needed. I found out that the del method isn’t working as long as the append function is in use. I also struggled to dereference the list in C the * operator is in use therefore.
Do you have any suggestions on this?

Here you can see my code:

class grocery:
    count=0
    liste=[]
    
    def __init__(self):
        print('Grocery created')
        #grocery.liste.append(self)
        grocery.count +=1

    def __del__(self):
      # grocery.liste.remove(self)
        print('Objekt gelöscht')
        grocery.count -=1

## creation
obj1= grocery()
obj2= grocery()
obj3= grocery()
obj4= grocery()
obj5= grocery()

##printing
print(grocery.count)
print(len(grocery.liste))

"""deleting"""
del obj1
print(grocery.count)

As you can see I have comment out the hamper thing. Do you have also a recommendation of a good book ?
Thank you Jürgen

Using del is notoriously buggy (when __del__ is called, depends on when the gc actually cleans up the object, which is implementation dependent).

It is a much better idea to change the API instead - add class methods to grocery, that add and remove grocery instances. These can easily track the instances using your existing code.

But FYI the issue is that there is still a reference to obj1 in grocery.liste:

Note

del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x’s reference count reaches zero.
3. Data model — Python 3.13.2 documentation

Perhaps the code will work with little modification by appending a weakref to self, to liste in __init__ (instead of self). But I’d highly recommend redesigning the class not to use __del__ at all:

My class defines del but it is not called when I delete the object.

A fix using weakref.ref (not the solution I recommend):

import weakref

class grocery:
    count=0
    liste=[]
    
    def __init__(self):
        print(f'Grocery created: {self}')
        self.ref = weakref.ref(self)
        grocery.liste.append(self.ref)
        grocery.count +=1

    def __del__(self):
        grocery.liste.remove(self.ref)
        print(f'Objekt gelöscht: {self}')
        grocery.count -=1

## creation
obj1= grocery()
obj2= grocery()
obj3= grocery()
obj4= grocery()
obj5= grocery()

##printing
print(f'{grocery.count=}')
print(f'{len(grocery.liste)=}')

"""deleting"""
del obj1
print(f'{grocery.count=}')

print('Program ending, gc being called in 3, 2, 1, ...')

The FAQ covers this too, in the very next question: How do I get a list of all instances of a given class?

1 Like

Thank you James for helping me. Now I understood the concept of the problem with the del method much better and I will experiment with weakref. :+1:

1 Like

After a lot of attempts to use the weakref proberly, I still struggle with my small programm which should collect generated objects in a list and finally print it to the user

import weakref

##mainclass
class person:
    person_in=0
    objekte=[]  
    
    def __init__(self, name, age, sex):
         self.ref = weakref.ref(self)
         self.name=name
         self.age=age
         self.sex=sex
         person.objekte.append(self.ref)
         person.person_in +=1

    def __del__(self):
        print(f'The person {self.name} was being destructed')
        person.objekte.remove(self.ref)
        person.person_in -=1
        
    
    def __iter__(self):
        return self

    def __next__(self):
        current = self.name
        self.name +=1
        return current

    def __str__(self):
        return f" Sex={self.sex}, Age={self.age}, Name={self.name}"

##inheritend child class
class child(person):
    
    def __init__(self, name, age, sex, mother, father, siblings):
        person.__init__(self, name, age, sex)
        self.mother=mother
        self.father=father
        self.siblings=siblings
        print(f"total number of objects (child class).= {person.person_in}")
        
##inheritend adole class
class adole(person):

    def __init__(self, name, age, sex, mother, father, siblings, friends):
        person.__init__(self, name, age, sex)
        self.mother=mother
        self.father=father
        self.siblings=siblings
        self.friends=friends
        print(f"total number of objects (adolesence class).= {person.person_in}")
       
## Create some objects
p1= person("Mike",62,"male")
p2= person("Doro",21,"female")
p3= person("Heinzi",56,"male")
p4= child("Babyface",6,"female","Mimi","Hirni",0)
p5= adole("Holo",12,"male", "Sarah", "John", 1,21)
p6= child("Dolly",2,"male", "Mother", "Father",2)

## Delete some objects
del p1,p2


print(f"total numbers of objects (outside).= {person.person_in}")


## print out the content of the objects which are still in then list
## unfortunately, the list holds only the memory adress where weakref is pointed at instead of the values in the list!
for x in range(person.person_in):
    print(person.objekte[x])`

What’s the issue? This is pretty much what I’d expect:`

total number of objects (child class).= 4
total number of objects (adolesence class).= 5
total number of objects (child class).= 6
The person Mike was being destructed
The person Doro was being destructed
total numbers of objects (outside).= 4
<weakref at 0x000002510FCF4590; to 'person' at 0x000002510FCD47D0>
<weakref at 0x000002510FCF45E0; to 'child' at 0x000002510FCA9160>
<weakref at 0x000002510FCF4630; to 'adole' at 0x000002510FCA92B0>
<weakref at 0x000002510FCF4680; to 'child' at 0x000002510FCD5090>
The person Heinzi was being destructed
The person Babyface was being destructed
The person Holo was being destructed
The person Dolly was being destructed

The four at the end are from the garbage collector kicking in. Check out super by the way.

Hello James, sorry for question you again, but I have still the same problem. The issue is actually that I want, after deleting some objects, to know who ist still in the list (objekte). Unfortunately, if i try to call the remaining objects from the list “objekte” I got only there weakref adresses but not e.g. there names and or other attributes.

import weakref

##mainclass
class person:
    person_in=0
    objekte=[]
        
    def __init__(self, name, age, sex):
         self.ref = weakref.ref(self)
         self.name=name
         self.age=age
         self.sex=sex
         person.objekte.append(self.ref)
         person.person_in +=1

    def __del__(self):
        print(f'The person {self.name} was being destructed')
        person.objekte.remove(self.ref)
        person.person_in -=1
        
    
    def __iter__(self):
        return self

    def __next__(self):
        current = self.name
        self.name +=1
        return current

    def __str__(self):
        return f" Sex={self.sex}, Age={self.age}, Name={self.name}"

       

##inheritend child class
class child(person):
    
    def __init__(self, name, age, sex, mother, father, siblings):
        super().__init__(name, age, sex)
        self.mother=mother
        self.father=father
        self.siblings=siblings
        print(f"total number of objects (child class).= {person.person_in}")
        
##inheritend adole class
class adole(person):

    def __init__(self, name, age, sex, mother, father, siblings, friends):
        super().__init__(name, age, sex)
        self.mother=mother
        self.father=father
        self.siblings=siblings
        self.friends=friends
        print(f"total number of objects (adolesence class).= {person.person_in}")
       
## Create some objects
p1= person("Mike",62,"male")
p2= person("Doro",21,"female")
p3= person("Heinzi",56,"male")
p4= child("Babyface",6,"female","Mimi","Hirni",0)
p5= adole("Holo",12,"male", "Sarah", "John", 1,21)
p6= child("Dolly",2,"male", "Mother", "Father",2)

## Delete some objects
del p2,p6

print(f"total numbers of objects (outside).= {person.person_in}")

##using the str methode- this is working
print(person.__str__(p4))

## here I tried to check if an object still exists- this is not working
    ##if p1 is in person.objekte:
    ##    print("p1 is still there")
    ##else:
    ##    print("p1 is not longer in the list)")

#who is still in the list?
## print out the objects (not the memory adress) which are still in objekte
for x in range(person.person_in):
    print(" in der Liste ist noch {}\n" .format(person.objekte[x]))

## is there a weakref command (how can I call thsi) that bring back the original object? Not a memory adress?
   

The problem is in the last part of the code where I struggled to call the remaining objects.
Thank you Jürgen

"The original object can be retrieved by calling the reference object if the referent is still alive; "

Hello,

as an alternative to the approach from your posted script, instead of using a list, you make use of a dictionary. You use the name of the family member as the key and the instance attributes collectively as the value. Note that the value here is itself a dictionary that contains all of the instance attributes with their assigned values.

Here is the test script. Test it and see if you can apply it to your application:

class Person:

    person_in = 0
    member = {}

    def __init__(self, name, city, sport):

        self.name = name
        self.city = city
        self.sport = sport

        Person.person_in += 1
        Person.member[name] = self.__dict__

    def __del__(self):
        Person.person_in -= 1

# Create instances
rob = Person('Rob', 'Chicago', 'Football')
ed = Person('Ed', 'Denver', 'Baseball')
sam = Person('Sam', 'Miami', 'Basketball')
bill = Person('Bill', 'Seattle', 'Hockey')

print('\nRaw dictionary values:')
print('---------------------')
print(Person.member)

print(f'\nStarting persons in roster; Qty: {Person.person_in}')
for values in Person.member.values():
    print('')
    for key, value in values.items():
        print(f'{key+':':<6} {value:<10}', end='')

# Remove Sam from the roster
del sam, Person.member['Sam']   # Delete object and from the dictionary

# Print members to verify changes were made
print(f'\n\nRemoved Sam from roster; Qty: {Person.person_in}')
for values in Person.member.values():
    print('')
    for key, value in values.items():
        print(f'{key+':':<6} {value:<10}', end='')

Output:

Raw dictionary values:
---------------------
{'Rob': {'name': 'Rob', 'city': 'Chicago', 'sport': 'Football'}, 'Ed': {'name': 'Ed', 'city': 'Denver', 'sport': 'Baseball'}, 'Sam': {'name': 'Sam', 'city': 'Miami', 'sport': 'Basketball'}, 'Bill': {'name': 'Bill', 'city': 'Seattle', 'sport': 'Hockey'}}

Starting persons in roster; Qty: 4

name:  Rob       city:  Chicago   sport: Football  
name:  Ed        city:  Denver    sport: Baseball  
name:  Sam       city:  Miami     sport: Basketball
name:  Bill      city:  Seattle   sport: Hockey    

Removed Sam from roster; Qty: 3

name:  Rob       city:  Chicago   sport: Football  
name:  Ed        city:  Denver    sport: Baseball  
name:  Bill      city:  Seattle   sport: Hockey 

From the test results, you see that after removing Sam from the roster, the changes are reflected in the second print iteration of the dictionary.

If you attempt to print the instance sam, an exception is generated:

    print(sam)
          ^^^
NameError: name 'sam' is not defined. Did you mean: 'sum'?

Attempt to print it from the list:

    print(Person.member['Sam'])
          ~~~~~~~~~~~~~^^^^^^^
KeyError: 'Sam'

It’s much simpler to make the person creating the objects responsible for maintaining a list of objects if one is actually needed. Person models a single person; don’t make it model a group of people as well.

class Person:
    def __init__(self, *, name, age, sex):
         self.name = name
         self.age = age
         self.sex = sex 
   
    def __str__(self):
        return f" Sex={self.sex}, Age={self.age}, Name={self.name}"


class Child(Person):
    def __init__(self, *, mother, father, siblings, **kwargs):
        super().__init__(**kwargs)
        self.mother = mother
        self.father = father
        self.siblings = siblings
        

class Adole(Person):
    def __init__(self, *, friends, **kwargs):
        super().__init__(**kwargs)
        self.friends = friends
       
## Create some objects
people = []
people.append(Person(name="Mike", age=62, sex="male"))
people.append(Person(name="Doro", age=21, sex="female"))
people.append(Person(name="Heinzi", age=56, sex="male"))
people.append(Child(name="Babyface", age=6, sex="female", mother="Mimi", father="Hirni", siblings=0))
people.append(Adole(name="Holo", age=12, sex="male", mother="Sarah", father="John", siblings=1, friends=21))
people.append(Person(name="Dolly", age=2, sex="male", father="Father", mother="Mother", siblings=2))


## Delete some objects
del people[0]  # This removes Mike, leaving Doro as the first person in the list
del people[0]  # This removes Doro

print(f"total numbers of objects.= {len(people)}")  # 4

for x in people:
    print(x)

Having a bunch of numbered variables like p1, p2, etc, is an anti-pattern, and should virtually always be replaced by a list or a dictionary. A dict is more verbose, but even simpler:

people = {}
people["mike"] = Person(name="Mike", age=62, sex="male")
people["doro"] = Person(name="Doro", age=21, sex="female")
people["heinzi"] = Person(name="Heinzi", age=56, sex="male")
people["babyface"] = Child(name="Babyface", age=6, sex="female", mother="Mimi", father="Hirni", siblings=0)
people["holo"] = Adole(name="Holo", age=12, sex="male", mother="Sarah", father="John", siblings=1, friends=21)
people["dolly"] = Person(name="Dolly", age=2, sex="male", father="Father", mother="Mother", siblings=2)

del people["mike"]
del people["doro"]

print(f"total numbers of objects.= {len(people)}")  # 4

for x in people:
    print(x)

Dear Clint Thanky ou very much- this has helped me a lot. I thought that my numbering wasn’t the best. Jürgen

Thank you James I that was exactly what I needed. Jürgeb

1 Like

Hello Paul, Thank you for this very useful. Dictionary seems to be a very good solution. Jürgen