Program design: search bar

Learn this with friends

ComputingIntro to computer science - Python6:476 check-ins

About this video

The video designs a shoe store search feature with a Shoe class whose __init__ stores name, color, brand and size. It puts the class in its own module, loops over a list of Shoe objects comparing a keyword to their attributes, avoids shadowing the module name with a loop variable, prints readable results with a format string, and makes matching partial and case-insensitive.

You'll be able to

  • write a class with an __init__ method and create objects with the right number of arguments.
  • search a list of objects by comparing a keyword to their attributes.
  • explain what shadowing is and how to avoid it.
  • use in and lower() to make a search partial and case-insensitive.

Key words

module
A separate Python file whose code, like a class definition, can be imported.
shadowing
Reusing a name, like a loop variable, so it hides an earlier thing with the same name.
memory address
The location where an object is stored, shown when printing an object Python doesn't know how to display.
case insensitive
Treating uppercase and lowercase letters as the same when comparing.
linter
A tool in the editor that warns about likely problems in your code.
Transcript

- [Instructor] Let's design a program with objects and attributes. We are building a site for our shoe store, where customers can browse through our product catalog. Customers want to filter their search results based on the kind of shoes they're looking for, so we need a program that can model the shoes in our catalog, and we need to search through those shoes to find the shoe that matches what the customer asked for.

Our catalog has hundreds of pairs of shoes, but every pair has some things in common. They all have a name, a color, a brand and a size. To represent a pair of shoes, the first thing we'll need is a class definition. I'll let the IDE auto complete this, and I'll name my data type Shoe.

Now, we need to initialize our shoe attributes, which we do in the init method. So we'll pass in as parameters initial values for name, color, brand and size. And then we store those values on the object, setting up our attributes so we can access those values later.

We know Python passes in the object through that first parameter self. So our init assignments always look like self.attribute name. Let's test that by creating a Shoe object. We know from our init method that we need to pass in four arguments when we create a shoe since we skip that first self parameter.

So let's say we have some Air Jordans in black and red made by Nike in a size 8, and let's just check that that data is getting stored by accessing our attributes after the object's created. Nice, no bugs in our class definition, so let's fill out the rest of our catalog.

To do the search, we're going to need to iterate over every shoe to check if it matches, so let's store our Shoe objects in a list. I'm not gonna input all of my product data just yet, but let's at least add a few more shoes. Now that I have this set up, one convenient code organization convention is to put each class definition in its own file.

Two reasons. One, like a function definition, a class definition must appear in the program before that class is called. And the easiest way to guarantee that is to import the class definition at the top. Two, it helps make our data type reusable. Any other code that needs to deal with shoe types can just import the file with the shoe class and not have to worry about other logic getting pulled in too.

Since the shoe type is now defined in the shoe module, we need to change these to module shoe dot class Shoe. Remember that dot syntax lets us access something inside something else, but this is a class inside a module, not an attribute inside an object. We have our data, now it's time for our search logic.

To break this down a bit, let's start by letting customers search by brand. They'll enter their search keyword and then we'll look through our shoes for a match. So we'll add a for loop where on each iteration our loop variable holds the next shoe object. So let's say they search for Nike, we wanna check if that keyword equals not the entire shoe object, but the brand attribute of that shoe.

So quick test. If I enter Nike, I should get two matches, and if I enter Reebok, I should get zero. Cool, what's the linter complaining about over here? It says import shoe from line 1 shadowed by loop variable. What do you think that means? Well, we created a variable named shoe, but we also have a module named shoe that we imported.

Python technically allows this, but it can only understand one concept shoe at a time. That means from here on out, Python no longer knows about the module shoe. If I try to create a shoe object after this line, I get an error because shoe now to Python means that loop variable, not the module.

So we wanna avoid shadowing the name shoe and name our loop variable something else. Now, when we do find a match, we wanna display information about that shoe to the customer, but when I print an object, it looks like this. When we define a custom data type, Python doesn't really know how to display it, so it just defaults to showing the type, and then this is a memory address.

It's the actual location where the object is stored in the computer's memory, not so useful to our customers. What we actually wanna show them is data about that shoe so we need to access the object's individual attributes, which we can combine together in a nice way using a format string.

So now we have our search and our search results, but we want our search feature to be a bit more powerful. We should be able to match that keyword against the name, the color or the brand, so let's compare our keyword against all three attributes of each shoe, but instead of an exact match, let's use the in operator to do a partial match on name and color.

That way, if the customer enters red, we would still match against this black/red color. And just to make this a bit smarter, let's do a case insensitive match, meaning we use the lower method to convert all of our strings to lowercase before we compare them so that the keyword lowercase nike still matches the brand capitalized Nike.

With that basic search logic working and our data modeled nicely in a class, we can easily build out more advanced features for our customers, letting them search by multiple keywords or filter by shoe size, that is once we finish the data entry process of adding the rest of our shoe catalog.

Video © Khan Academy, used under CC BY-NC-SA. Watch on Khan Academy. Puzzicle is not affiliated with Khan Academy.