getattr lookup fails for instance of class python

1.5k views Asked by At

I'm doing an exercise in which you're supposed to calculate a score of how classy your items are. A tophat gives you 2 points, a bowtie gives you 4 points and a monocle gives you 5 points. I've initiated a dictionary with these items on each class instance, yet when I use getattr to check if the item has the attribute it always returns None.

class Classy(object):

    def __init__(self):
        self.items = []
        self.classyItems = {'tophat': 2, 'bowtie': 4, 'monocle': 5}

    def addItem(self, str):
      self.items.append(str)

    def getClassiness(self):
      classiness = 0
      for item in self.items:
        itemIsClassy = getattr(self.classyItems, item, None) # Why does this always return none?
        if itemIsClassy:
          classiness += itemIsClassy
        else:
          pass
      return classiness


# Test cases
me = Classy()

# Should be 0
print me.getClassiness()

me.addItem("tophat")
# Should be 2
print me.getClassiness() # I'm getting 0

Why is getattr returning None when self.classyItems clearly has the attribute of tophat?

Thanks for the help everyone! I was still confused after reading the answer and simply reading that dictionary keys are not attributes here helped me. Is Python dict an Object?

4

There are 4 answers

0
MSeifert On BEST ANSWER

getattr accesses attributes and/or methods, but classyItems is a dict which doesn't have any (visible) attributes1 and stores its contents as key-value pairs2.

In your case you should use dict.get instead of getattr, more specifically self.classyItems.get(item, None), to access the values with default.

1Dictionaries have methods that could be accessed using getattr though.

2That's why you access them using d[key] instead of d.key.

0
Dharmesh Fumakiya On

Replace itemIsClassy = getattr(self.classyItems, item, None) to itemIsClassy = self.classyItems.get(item, None)

0
Chen A. On

getattr() takes an object and a string of the attribute to look for, and returns that object. You don't have an attribute named item in your object, but you do have classyItems: Using your code you can see that:

>>> getattr(me, 'classyItems')
{'bowtie': 4, 'tophat': 2, 'monocle': 5}

It is much easier (and readable) to use self.classyItems.get(item, None) which looks up a dict and returns default value. You can't lookup a dict values with getattr, but you can however check if it has certain methods:

>>> getattr(me.classyItems, 'update')
<built-in method update of dict object at 0x028ADE40>
0
b4oshany On

As mentioned by @Dharmesh and @MSeifert, replace itemIsClassy = getattr(self.classyItems, item, None) to itemIsClassy = self.classyItems.get(item, None)

The reason why your code didn't work is not because classyItems doesn't have many attributes, it is because the getattr function works on objects, not dictionary.