AttributeError w/ Class Hierarchies

Hello! I am currently working on the final exercise for the class module in the intro to python course, but I am running into an AttributeError for step 17.
Here is my code:

class Franchise(): def __init__(self, address, menus): self.address = address self.menus = menus def __repr__(self, address): return self.address def available_menus(self, time): available_menus = [] for menu in self.menus: if self.start_time <= time and self.end_time >= time: available_menus.append(menu) return available_menus class Menu(): def __init__(self, name, items, start_time, end_time): self.name = name self.items = items self.start_time = start_time self.end_time = end_time def __repr__(self): return '{} menu available from {} to {}'.format(self.name, self.start_time, self.end_time) def calculate_bill(self, purchased_items): bill = 0 for purchased_item in purchased_items: if purchased_item in self.items: bill += self.items[purchased_item] return bill brunch_items = {'pancakes': 7.50, 'waffles': 9.00, 'burger': 11.00, 'home fries': 4.50, 'coffee': 1.50, 'espresso': 3.00, 'tea': 1.00, 'mimosa': 10.50, 'orange juice': 3.50 } kids_items = { 'chicken nuggets': 6.50, 'fusilli with wild mushrooms': 12.00, 'apple juice': 3.00 } brunch_menu = Menu('Brunch', brunch_items, 1100, 1600) kids_menu = Menu('Kids', kids_items, 1100, 2100) menus = [brunch_menu, kids_menu] flagship_store = Franchise('"1232 West End Road"', menus) flagship_store.available_menus(1200)

Can anyone figure out why this might be happening? As far as I can tell, my code looks identical to what is demonstrated in the walkthrough video…

Also, I am unsure how Franchiese gets access to the menus…

Thanks in advance!

Look carefully at your __init()__ method for the Franchise class. Is there an attribute called start_time?

class Franchise():
  def __init__(self, address, menus):
   self.address = address
   self.menus = menus   

Can you see any start_time attribute here?

You are correct to point out that there is no start_time in my __init__, but the solution video did not include this either…so I figured that possibly Franchiese could be somehow inheriting from Menu - but how this could be is not obvious to me.

When using self in your available_menus function what should that be referring to? Consider carefully what self refers to at that point in time…

class Franchise()...
    def available_menus:
        for menu in self.menus:
            if self.start_time <= time and self.end_time >= time:

Perhaps certain objects do have the attributes you’re trying to access?

It’s not directly relevant to this problem but there’s a difference in how __repr__ is intended to be used and how __str__ should be used which annoys me in the cc lessons. In this example it should really be __str__ for an informal representation, __repr__ is geared towards debugging (ideally you could re-create the same object using the return from __repr__).

1 Like

Oh! Thanks for pointing that out. I think I understand now that self in self.menus would be referring to the particular menu that was passed. If this is the case, then I think my confusion is cleared up - let me know if I am still misunderstanding, though!

Ah, that’s where the confusion is then. Since that function definition is part of, and gets bound to, the class definition Franchise, that self will refer to objects created by Franchise. So it’s referring to Franchise objects rather than Menu objects.

So your query about how does Franchise access attributes from Menu objects is perfectly valid and clarified further by @codeneutrino’s point. Put simply put, it doesn’t.

What we have are a selection of objects created from Menu that do have .start_time and end_time attributes. For example brunch_menu-

brunch_menu = Menu('Brunch', brunch_items, 1100, 1600)
print(brunch_menu.name)

With that information can you see how to access the relevant values?

Although class notation and objects can be a bit weird at first you may already be somewhat familiar with them. Consider lists, if you create a list object you can add new names to the same object; you can also add it as an attribute to other objects e.g. our class defined instance objects (the behaviour is similar to other assignments).

class Test():
    def __init__(self, a_list):
        self.a_list = a_list


x = [1, 2, 3]
y = x  # we can assign an additional name to same object
test_obj = Test(x)  # __init__ also binds it to the new object
# just to stress the point, add another attribute-
test_obj.b_list = x

Say you now wanted to update the list at test_obj.a_list with an additional value in that sequence, an integer 4. Our Test class doesn’t know much about the object, but the bound object itself has references to its own attributes; we’d probably use- test_obj.a_list.append(4).

Technically since we have four references to the same object x.append(4), y.append(4) and test_obj.b_list.append(4) also all do exactly the same thing but the point about attributes remains. You can treat such an attribute like a normal reference. If you want to access values from the list use it like a list, if you want values from a Menu instance, use it like a menu object.

1 Like