Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Object-Oriented Python Advanced Objects Constructicons

Jay Reyes
seal-mask
.a{fill-rule:evenodd;}techdegree
Jay Reyes
Python Web Development Techdegree Student 15,937 Points

Bookcase's 'books' attribute

Is the 'books' attribute here:

class Bookcase:
    def __init__(self, books=None):
        self.books = books

... redefined (for lack of a better word) into a list here?

@classmethod
    def create_bookcase(cls, book_list):
        books = []
        for title, author in book_list:
            books.append(Book(title,author))
        return cls(books)

And we are not using self.books = [] because scope of classmethod should not use instantiated attributes, right?

I'm basing this question off the assumption that we are not trying to store a Bookcase class in memory. However, Kenneth says cls(books) will run Bookcase __init__...

4 Answers

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,720 Points

Your question shows a high level of understanding of Kenneth's point. And you are correct in your assumption of how the Bookcase class functions as an "abstracted" list container. Kenneth is setting the student up to recognize a recurring design pattern. That we are not instantiating a self.books = [] is in keeping with his idea of useful abstraction.

In this case, the Bookcase class is starting to look like a native Python "list" of objects. Kenneth will subsequently show how you can define a class as a child of a native Python data structure. The benefits are pretty huge because we inherit a great deal of fully functional code-- sort(), len(), push(), pop(), etc. that can be used by our user-defined class rather than having to manipulate an embedded "self" datastructure. As a side-benefit our code suddenly becomes more reuseable and understandble to others.

Real-world developers recognize the limitations of "top-down" design approaches they might have encountered in Computer Science/Information Technology training as a useful model but a little impractical when it comes to writing efficient code. Since as developers we rarely design things entirely from scratch, but choose to use libraries, APIs, ORMs, and frameworks.

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,720 Points

To directly answer your question: The classmethod create_bookcase is an __init__ method of sorts (you can also think of it as a factory). That empty list is being created and built Before the __init__ method on cls object is called.

You can prove this to yourself by putting print() statements into each method and see the order in which it is being resolved.

A slightly deeper explanation

In this case, Kenneth is setting the groundwork for a "factory" object pattern. And further in the course, we will modify the Bookcase object a little further itself to be a child class of a list. This will give us a lot of "free" functionality.

For now, let's focus on the idea of Factory

Think of the Bookcase class (classmethod) as a factory with an assembly line and a packaging facility encapsulated within.

The create_bookcase(cls, book_list) first starts with an empty list and then the for loop block is the "assembly line". It builds a list of Book objects matching the instructions/orders sent in book_list. The book_list contains tuples which specify title and author of a Book to be instantiated (or loaded) into our books list.

Let's say we're starting a very small new library and we want a bookcase devoted to children's classics and another bookcase devoted to classic fiction.

# Our library puts in an order to the Bookcase factory two orders
books_for_children_order = [
    ('Cat in the Hat', 'Dr. Suess'), 
    ('Hop on Pop','Dr. Suess'), 
    ('Clifford the Big Red Dog', 'Norman Bridwell')
    ]
books_for_classics_order = [
    ('Moby-Dick', 'Herman Melville'),
    ('David Copperfield', 'Charles Dickens'),
    ('Wuthering Heights', 'Emily Bronte')
]

Now, we send in the orders to the Bookcase factory.

childrens_bookcase = Bookcase.create_bookcase(books_for_children_order)
classics_bookcase = Bookcase.create_bookcase(books_for_classics_order)

Inside the factory-- the list "books_for_children_order" is given to the assembly_line loop of create_bookcase

When the assembly line is finished, it sends the books (think of it as a box containing all the books in the order) to the the packaging facility which is actually the __init__ method of cls. The instantiated cls is returned as the "shiny new bookcase" pre-filled with our book orders.

We can then repeat that for the classics book order.

Our Library benefits in that it can call the factory with more orders later on!

Disclaimer-- this could be done in many different (and correct) ways. You are the one in control. Kenneth wanted us to experience the factory pattern and show you can make your objects into factories if it makes sense. I can guarantee as you progress, you will encounter this pattern quite a bit.

Good luck with your Python journey!!

So this classmethod essentially gives the user another way to input data to use the class?

Hopefully my understanding of this is clear, but would the list books defined in create_bookcase be a class variable? And then the self.books in the init() method is calling that class variable?

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,720 Points

Nicholas, you definitely are understanding this correctly. The classmethod is kind of like a class "factory" or class "cloning" function.

Can you elaborate a little more on this point? I understand that classmethods are used to work with classes rather than instances, but what is the relationship between self.books as an attribute and books as an empty list defined later on?