root@coding-prodigies:~# โ–Š
// lesson 2 of 11 ยท 16 min

Classes and objects

โš‘ Report an issue with this lesson

A class is a blueprint. An object (instance) is one specific thing built from that blueprint.

class Course:
    def __init__(self, title, price):
        self.title = title
        self.price = price

    def describe(self):
        return f"{self.title} -- ${self.price}"

html_course = Course("HTML Fundamentals", 29.99)
print(html_course.describe())  # HTML Fundamentals -- $29.99

__init__ runs when you create an object and sets up its starting state. self refers to the specific object the method was called on โ€” every method that touches instance data takes self as its first parameter.

Each call to Course(...) creates a completely independent object with its own copy of the attributes set inside __init__. Two Course instances never share title or price unless you explicitly make one point at the other:

c1 = Course("HTML Fundamentals", 29.99)
c2 = Course("SQL Databases", 59.99)

c1.price = 19.99   # only changes c1
print(c2.price)    # still 59.99 -- completely separate object

This is different from a class-level attribute โ€” one defined directly in the class body, outside any method โ€” which is shared across every instance until an individual instance overrides it:

class Course:
    platform = "Coding Prodigies"  # shared by every Course instance

    def __init__(self, title, price):
        self.title = title    # unique to each instance
        self.price = price

c1 = Course("HTML", 29.99)
c2 = Course("SQL", 59.99)
print(c1.platform)  # "Coding Prodigies"
print(c2.platform)  # "Coding Prodigies" -- same value, same class attribute

A common mistake for anyone coming from another language is forgetting self โ€” either leaving it off a method's parameter list (Python then passes the instance as the first positional argument anyway, silently binding it to whatever you named first, which produces confusing errors), or forgetting to write self. before an attribute inside a method, which creates a plain local variable that disappears when the method returns instead of actually updating the object:

class Course:
    def __init__(self, title, price):
        self.title = title
        self.price = price

    def apply_discount(self, percent):
        price = self.price * (1 - percent / 100)  # BUG: local variable, not saved
        # should be: self.price = self.price * (1 - percent / 100)

Calling apply_discount(10) above looks like it worked โ€” no error, no crash โ€” but course.price is completely unchanged afterward, because price without self. is just a throwaway local name. This is one of the most common silent bugs in early object-oriented Python code, precisely because nothing about it raises an exception.

Try it yourself

Exercise: Add a describe() method to the Course class below that returns f'{self.title} -- ${self.price}', then create html_course = Course('HTML Fundamentals', 29.99) and print its description.
Expected output:
HTML Fundamentals -- $29.99
python
Output

      
    

Run your code and get it working before marking this lesson complete.

// free preview

8 more lessons โ€” including Project: build a small shape library โ€” plus a certificate are waiting.

Unlock the full course โ€” $59.99