user2818751
user2818751

Reputation: 33

understanding this Python script

I got this code and i'm trying to figure out how this works

class Animal(object):
    population = 0
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return "I am an instance of {}. ".format(self.__class__, self.name)
    def __repr__(self):
        return self.__str__()

    def make_sound(self):
        return "{} is trying to speak, but its method doesn't do much".format(self.name)

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed
    def __str__(self):
        print(super().__str__())
        return "My breed is {}".format(self.breed)

    def make_sound(self):
        return "{} says woof!".format(self.name)

class Cat(Animal):
    pass


animals = {'Felix': ('Cat', None), 'Fido': ('Dog', 'mutt'), 'Charlie': ('Dog', 'spaniel')}
animals_list = []
for k in animals:
    if animals[k][1]:
        animals_list.append(globals()[animals[k][0]](k, animals[k][1]))
    else:
        animals_list.append(globals()[animals[k][0]](k))
    Animal.population+=1

for animal in animals_list:
    print(animal)
    print(animal.make_sound())
print("Animal population is {}".format(Animal.population))

How are the classes initiated? I can't get my head around how this is done, seems the classes dog and cat are already initiated when the script reaches the if animals part.

Thanks in advance

Upvotes: 0

Views: 135

Answers (1)

dano
dano

Reputation: 94871

The classes are instantiated inside this for loop:

for k in animals:
    if animals[k][1]:
        animals_list.append(globals()[animals[k][0]](k, animals[k][1]))  # Instantiates a class
    else:
        animals_list.append(globals()[animals[k][0]](k))  # Instantiates a class

So, if k is 'Felix', this code is saying:

if None: # Because animals['Felix'][1] is None
    animals_list.append(globals()['Cat']('Felix', None))
else:
    animals_list.append(globals()['Cat']('Felix'))

globals() returns a dictionary containing the name -> object mapping of all variables in the global namespace. So, globals()['Cat'] returns the actual Cat class. That means globals()['Cat']('Felix') is equivalent to Cat('Felix'). And that, obviously, is instantiating the Cat class with the name 'Felix'. This same pattern repeats for every entry in the animals dict, so in the end, the animals_list contains the instances returned by calling Cat('Felix'), Dog('Fido', 'mutt'), and Dog('Charlie', 'spaniel').

And for what its worth, I agree with the commentors who have pointed out this code is quite ugly. It makes much more sense to do this:

animals = {'Felix': (Cat, None), 'Fido': (Dog, 'mutt'), 'Charlie': (Dog, 'spaniel')}
animals_list = []
for k in animals:
    if animals[k][1]:
        animals_list.append(animals[k][0](k, animals[k][1]))
    else:
        animals_list.append(animals[k][0](k))

No need to use globals().

Upvotes: 1

Related Questions