I'm learning about Classes and this thing is driving me crazy. Whenever I run this, it will use the del object even though I never used del droid1, del droid2, or del droid3. Can someone please help?

class Robots:
    '''Represents a robot, with a name.'''

    #A class variable, counting the number of robots
    population = 0

    def __init__(self, name):
        '''Initializes the data'''
        self.name = name
        print("(Initializing {0})".format(self.name))

        #When this person is created, the robot adds to the population
        Robots.population += 1

    def __del__(self):
        '''I am dying'''
        print("{0} is being destoryed!".format(self.name))

        Robot.population -= 1

        if Robot.population == 0:
            print("{0} was the last one.".format(self.name))
        else:
            print("There are still {0:d} robots working".format(Robot.population))


    def sayHi(self):
        '''Greeting by the robot

        yeah, they can do that.'''
        print("Greetings, my masters call me {0}".format(self.name))

    def howMany():
        '''Prints the current population'''
        print("We have {0:d} robots.".format(Robots.population))
    howMany = staticmethod(howMany)

droid1 = Robots("r2-d2")
droid2 = Robots("c-3po")
droid3 = Robots("SAL")

droid1.sayHi()
droid2.sayHi()
droid3.sayHi()

So first, Robot is not a defined variable anywhere, so Robot.population means nothing.

Second, the way you are using population wont work. Every time you make a new robot, the population will reset to 0, and only that robot will have a population of 0.

You will need to rethink how your class is going to work, and if you want to keep track of how many instances exist of the robot class, you will either need a global variable (or at least a variable outside of the class itself), or the instantiation of the class will add to a variable in the module.

You have only the Robots instead of Robot as name error, otherwise everything seems fine.

class Robot:
    '''Represents a robot, with a name.'''

    #A class variable, counting the number of Robot
    population = 0

    def __init__(self, name):
        '''Initializes the data
        '''
        self.name = name
        print("(Initializing {0})".format(self.name))

        #When this person is created, the robot adds to the population
        Robot.population += 1

    def __del__(self):
        '''I am dying
        '''
        print("{0} is being destoryed!".format(self.name))

        Robot.population -= 1

        if Robot.population == 0:
            print("{0} was the last one.".format(self.name))
        else:
            print("There are still {0:d} Robot{1} working".format(Robot.population, ('s' if Robot.population > 1 else '')))


    def say_hi(self):
        '''Greeting by the robot
        yeah, they can do that.
        '''
        print("Greetings, my masters call me {0}".format(self.name))

    @staticmethod
    def how_many():
        '''Prints the current population
        '''
        print("We have {0:d} Robot.".format(Robot.population))

droid1 = Robot("r2-d2")
droid2 = Robot("c-3po")
droid3 = Robot("SAL")

droid1.say_hi()
droid2.say_hi()
droid3.say_hi()
Robot.how_many()

del droid2
droids = [droid1, droid3]
del droid1
# population does not change as droid1 reference still exist
Robot.how_many()
del droids[0]
Robot.how_many()

I agree with tony that everything is fine if you replace Robots by Robot. However my advice is not to define objects with a __del__() method because they can create uncollectable objects if they are part of a reference cycle (read the documentation of gc.garbage about this). There are other techniques if you want to keep track of the number of living robots, for example use a weakref.WeakValueDictionary with the object's id() as key. The number of robots would be the length of this dictionary.
Notice that the python documentation says explicitely that you don't know when an object is collected, which means that you can't predict when python's garbage collector will call your __del__() method after the objects vanishes from the program's namespace, although in practice, the object seems to be collected immediately.
Also there is no guarantee that the __del__() method will be called at all, which is another reason not to use this method. In your case, the method is called when the memory is freed at interpreter's exit.

Edited 4 Years Ago by Gribouillis

:(
I got schooled... Oh well, at least I learned something.

However, wouldnt my method work?

Declaring the variable outside the class, it would store adds or subtracts every time an object was created or deleted?

Edited 4 Years Ago by ryantroop

However my advice is not to define objects with a del() method because they can create uncollectable objects if they are part of a reference cycle

Yes, but since his robots don't contain references to each other, that can't happen here.

Also there is no guarantee that the del() method will be called at all, which is another reason not to use this method.

It's only not guarantued if the object is still alive at the end of the program, in which case it wouldn't matter for this use-case since there's no way to callRobot.howMany() after the program ended.

Yes, but since his robots don't contain references to each other, that can't happen here.

I agree with that, but may be next year he will want to add a list to the robots instances, which contains Foo instances which in turn refer to other Robots, and here is the memory leak. It's almost impossible to ensure that there are no cyclic references in a python program :) . My rule is safer, one can live without del methods.

Edited 4 Years Ago by Gribouillis

I would be happy in one list of robots (if not only list of names) and using simple len() for the population info (without __del__ method in Robot) being a old fossil, but of course then you would not learn any OO from that. Even mostly I would avoid staticmethod methods, but maybe they have place when class variable is used and instance is not used inside the method (instead of using normal function)

Here is an implementation with the WeakValueDictionary. It has the same result as the __del__ method, but, as far as I know, there is no garbage collection issue.

#!/usr/bin/python3
# -*-coding: utf8-*-

from functools import partial
import weakref

def del_robot(name, wref):
    '''A robot is dying'''
    print("{0} is being destroyed!".format(name))
    population = len(Robot._living_instances)
    if population:
        print("There are still {0:d} robots working".format(population))
    else:
        print("{0} was the last one".format(name))

class Robot:
    '''Represents a robot, with a name.'''

    #A class variable, counting the number of Robot
    _living_instances = weakref.WeakValueDictionary()

    def __init__(self, name):
        '''Initializes the data'''
        self.name = name
        self._wref = weakref.ref(self, partial(del_robot, self.name))
        self._living_instances[id(self)] = self
        print("(Initializing {0})".format(self.name))

    def sayHi(self):
        '''Greeting by the robot

        yeah, they can do that.'''
        print("Greetings, my masters call me {0}".format(self.name))

    @classmethod
    def howMany(cls):
        '''Prints the current population'''
        print("We have {0:d} Robots (some of them may be dead already, but it is unlikely).".format(
            len(cls._living_instances)))

    @classmethod
    def reliable_count(cls):
        return len(cls._living_instances.values())

droid1 = Robot("r2-d2")
droid2 = Robot("c-3po")
droid3 = Robot("SAL")

droid1.sayHi()
droid2.sayHi()
droid3.sayHi()

del droid1
Robot.howMany()

"""My output -->
(Initializing r2-d2)
(Initializing c-3po)
(Initializing SAL)
Greetings, my masters call me r2-d2
Greetings, my masters call me c-3po
Greetings, my masters call me SAL
r2-d2 is being destroyed!
There are still 2 robots working
We have 2 Robots (some of them may be dead already, but it is unlikely).
c-3po is being destroyed!
There are still 1 robots working
SAL is being destroyed!
SAL was the last one
"""

Edited 4 Years Ago by Gribouillis

This article has been dead for over six months. Start a new discussion instead.