0

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

6
  • 5
    That is some ugly code. There was absolutely no reason to use the animal type as a string like 'Cat' and then look it up in globals(), when you can just use the name Cat instead. Commented Aug 21, 2014 at 20:21
  • What is it you don't understand? Have in mind that the part that begins with the animal dictionary assigning is the first to run Commented Aug 21, 2014 at 20:21
  • 1
    animals[k][0] is the name of the class, globals()['Cat'] returns the Cat class,, the rest are constructor parameters. Commented Aug 21, 2014 at 20:22
  • 1
    +1. Perfectly cromulent programming question with a clear ask and a reasonable position for confusion. Downvoters: j'accuse! Commented Aug 21, 2014 at 20:24
  • @kojiro quelle est la signification du mot 'cromulent' ? Commented Aug 22, 2014 at 10:20

1 Answer 1

1

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().

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.