Basta Fazoolin' question

Hi,

I’m doing the Basta Fazoolin’ project. Here is my original code:

import datetime

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 self.name+':'+' Available from '+str(self.start_time)+' until '+str(self.end_time)

brunch = Menu('Brunch', {
  '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
}, datetime.time(11), datetime.time(16))

early_bird = Menu('Early-bird Dinner', {
  'salumeria plate': 8.00, 'salad and breadsticks (serves 2, no refills)': 14.00, 'pizza with quattro formaggi': 9.00, 'duck ragu': 17.50, 'mushroom ravioli (vegan)': 13.50, 'coffee': 1.50, 'espresso': 3.00,
}, datetime.time(15), datetime.time(18))

dinner = Menu('Dinner', {
  'crostini with eggplant caponata': 13.00, 'ceaser salad': 16.00, 'pizza with quattro formaggi': 11.00, 'duck ragu': 19.50, 'mushroom ravioli (vegan)': 13.50, 'coffee': 2.00, 'espresso': 3.00,
}, datetime.time(17), datetime.time(23))

kids = Menu('Kids', {
  'chicken nuggets': 6.50, 'fusilli with wild mushrooms': 12.00, 'apple juice': 3.00
}, datetime.time(11), datetime.time(21))

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

  def __repr__(self):
    return self.address

  def available_menus(self, time):
    menus = []
    for menu in self.menus:
      if time >= menu.start_time and time < menu.end_time:
        menus += menu.name
    return menus

flagship_store = Franchise("1232 West End Road", [brunch, early_bird, dinner, kids])

new_installment = Franchise("12 East Mulberry Street", [brunch, early_bird, dinner, kids])

print(flagship_store.available_menus(datetime.time(12))

The result I get is:
[‘B’, ‘r’, ‘u’, ‘n’, ‘c’, ‘h’, ‘K’, ‘i’, ‘d’, ‘s’]

(I changed the method to:

def available_menus(self, time):
    menus = []
    for menu in self.menus:
      if time >= menu.start_time and time < menu.end_time:
        menus.append(menu.name)
    return menus

And got the result I wanted - [‘Brunch’, ‘Kids’].

I am trying to understand why my original code iterated through the letters of the item in the list and printed each letter separately. The for loop was for the items in the list, which are words, so why does it iterate through each letter of each word?

To investigate more I’d check the types by printing them.
General rule of thumb is that the += operator shouldn’t be relied on for a generic add of any type to a list. You want to use append for that (to append to the tail of the list).
You can use += for combining lists together.

Examples:

>>> x = []
>>> x += 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> x += [2]
>>> x
[2]
>>> x += "string"
>>> x
[2, 's', 't', 'r', 'i', 'n', 'g']
>>> x = [2,3]
>>> x += [4,5]
>>> x
[2, 3, 4, 5]
>>> x.append([6,7])
>>> x
[2, 3, 4, 5, [6, 7]]

Also, final note: it’s a little tricky to name your variable menus when you have a self.menus already… I’d pick a better variable name (for yourself reading your own code later and especially other people).

1 Like

I just worked through this issue yesterday, the easiest fix to this code would be:

menus += [menu.name]

This way you’re adding a list to a list instead of a string to a list.
I ended up formatting my function variable as a string to which I += menu strings and return.

1 Like

the end result might appear the same but under the hood this creates a new list that’s the result of menus + [menu.name]

Note the run times get worse as you scale up, so you really want to avoid this.

Example:

lst = [1,2]
lst += [3]
[Finished in 1.7s]

lst = [1,2]
lst.append(3)
[Finished in 1.1s]

But let’s what happens when it’s larger

lst = [0 for x in range(10000000)]
second_lst = [0 for x in range(10000000)]
lst += second_lst
[Finished in 9.8s]

lst = [0 for x in range(10000000)]
second_lst = [0 for x in range(10000000)]
lst.append(second_lst)
[Finished in 7.2s]

Note that the discrepancy will grow bigger in execution as the numbers grow. Python is a fairly slow program to begin with, so it’s good to know the pitfalls in speed where possible.

2 Likes

I wanted to expand the functionality of the method. I keep staring at it thinking that it’s clumsy, thoughts?

  def available_menus(self, mtime = datetime.now().time()):
    #currently_serving = "We are currently serving:\n"
    currently_serving = []
    for menu in self.menus:
      if mtime >= menu.start_time and mtime < menu.end_time:
        #currently_serving += menu.name + " Menu\n"
        currently_serving.append(menu)
    if len(currently_serving) == 0:
      earliest_menu = time.max
      name_menu = ""
      for opening in self.menus:
        if opening.start_time < earliest_menu:
          earliest_menu = opening.start_time
          name_menu = opening.name
      return f"We are currently closed, please return at {earliest_menu.strftime('%I:%M %p')} for our {name_menu} Menu."
    else:
      serving = "We are currently serving: \n"
      for i in range(len(currently_serving)):
        serving += currently_serving[i].name + " Menu \n"
      return serving

Seems ok at first glance. One way to check for refactoring is to write it out in pseudocode on paper. It’s always good to consider edge cases as well (empty values, base cases, etc.)

1 Like