Python • Object-Oriented Programming

Instance Attributes & Methods — Define instance attributes and methods; use self correctly

Learn how Python objects carry their own data and behaviors through instance attributes and methods.

Code braces with terminal and markup symbols representing Python programming.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

Before object-oriented programming (OOP) existed, programmers wrote code as long sequences of instructions with data stored in separate, unconnected variables. As programs grew larger, keeping track of which data belonged to which part of the program became a nightmare. Imagine trying to manage the stats for 500 video-game characters when every attribute — health, speed, name — is just a loose variable floating around. OOP emerged to solve this chaos by bundling data and the functions that operate on that data into a single unit called an object.

1967 • Simula Introduces Objects

Norwegian computer scientists Ole-Johan Dahl and Kristen Nygaard created Simula, the first language to feature classes and objects, originally designed to model real-world simulations.

1972 • Smalltalk Refines OOP

Alan Kay's team at Xerox PARC developed Smalltalk, which made everything an object and introduced the concept of message passing — the ancestor of method calls.

1991 • Python Is Born

Guido van Rossum released Python 0.9.0, which included classes with inheritance. Python's OOP model drew from C++ and Modula-3, emphasizing readability and simplicity.

2000s • Python Becomes Mainstream

Python's clean OOP syntax made it a favorite for teaching programming. The explicit use of 'self' became one of Python's most distinctive and debated design choices.

The core question that instance attributes and methods address is straightforward: how does each individual object keep track of its own unique data, and how does Python know which object's data to use when you call a method? The answer lies in a special keyword — self — which acts as each object's personal name tag, pointing back to itself so Python always knows whose data it's working with.

SECTION 2

Core Principles & Definitions

To understand instance attributes and methods, you first need to grasp a few foundational ideas. A class is a blueprint that describes what data an object will hold and what actions it can perform. An instance is a specific object built from that blueprint — like how a particular house is built from an architectural plan. Each instance can have its own unique values even though they all share the same structure.

1

Instance Attribute

A variable that belongs to one specific object. Each instance stores its own copy. Defined inside __init__ using self.attribute_name.
2

Instance Method

A function defined inside a class that operates on one specific object. Its first parameter is always self, which gives it access to that object's attributes.
3

The self Parameter

A reference to the current instance. Python automatically passes the object as the first argument when you call a method, so you never write it in the parentheses yourself.
4

The __init__ Method

A special method (called a constructor) that Python runs automatically when you create a new object. This is where you set up instance attributes with their initial values.
✦

KEY TAKEAWAY

Think of a class like a student ID card template — it has blank fields for name, grade, and student number. Each actual card (instance) gets filled in with a specific student's information (instance attributes). The self keyword is like the word "my" on the card — when a card says "my name," it always refers to the person holding that particular card.

SECTION 3

Visual Explanation — How self Connects Objects to Their Data

The diagram below shows how a single class blueprint produces two separate instances, each with its own data. Notice how self inside each method always refers back to the specific instance that called it. When dog1.bark() is called, self points to dog1; when dog2.bark() is called, self points to dog2.

Class Blueprint → Instance Objectsclass Dogdef __init__(self, name, age):self.name = nameself.age = agedef bark(self):return f"{self.name} says Woof!"Dog("Buddy", 3)Dog("Luna", 5)dog1 (Instance 1)self.name = "Buddy"self.age = 3bark() → "Buddy says Woof!"self → dog1dog2 (Instance 2)self.name = "Luna"self.age = 5bark() → "Luna says Woof!"self → dog2Each instance has its own copy of attributes; self always points to "this" object
The class Dog defines the blueprint. Two instances — dog1 and dog2 — each store their own name and age. The self reference inside each instance always points back to that particular object.

SECTION 4

How It Works — The Mechanics of self

Understanding the mechanism behind self is the key to mastering instance attributes and methods. When you write dog1.bark(), Python secretly translates this into Dog.bark(dog1). The object before the dot gets passed as the first argument to the method — and that first parameter is self. This is why every instance method must list self as its first parameter, even though you never pass it yourself when calling the method.

The __init__ Constructor

The __init__ method (pronounced "dunder init") is a special method that runs automatically the moment you create a new instance. Its job is to initialize instance attributes by attaching data to self. For example, self.name = name takes the value passed in through the name parameter and stores it as an attribute on the specific object being created. Without self, the variable would be local to the method and would disappear once __init__ finishes running.

Writing Instance Methods

An instance method is any function defined inside a class that takes self as its first parameter. Inside the method, you can read or modify instance attributes using self.attribute_name. Methods can also accept additional parameters after self — just like regular functions. The critical rule is: always use self to access or modify the object's data inside a method.

⚠

Common Mistake Alert

Forgetting self in a method definition is the #1 beginner error. If you write def bark(): instead of def bark(self):, Python will raise a TypeError saying it received 1 argument but expected 0. That "extra" argument is the instance Python tried to pass in!

SECTION 5

Anatomy of a Class — Detailed Breakdown

Let's dissect a complete class to see exactly where each piece fits. The diagram below annotates every part of a Student class, showing the relationship between the class definition, the constructor, instance attributes, and instance methods.

Anatomy of a Python Classclass Student:"""Represents a high school student."""def __init__(self, name, grade, gpa):self.name = nameself.grade = gradeself.gpa = gpaself.courses = []# default empty listdef enroll(self, course_name):self.courses.append(course_name)# modifies instance attributedef is_honor_roll(self):return self.gpa >= 3.5# reads instance attributedef summary(self):return (f"{self.name}, Grade {self.grade}, " f"GPA: {self.gpa}")# returns formatted stringCONSTRUCTOR (__init__)Runs when object is created.Sets up instance attributes.INSTANCE ATTRIBUTESUnique data for each objectINSTANCE METHOD (modifier)Changes the object's dataINSTANCE METHOD (accessor)Reads data, returns a resultINSTANCE METHOD (display)Formats object info as stringEvery self.x line creates or accesses an attribute that belongs to the specific instance
Annotated Student class showing the constructor (purple), instance attributes (amber), a modifier method (cyan), an accessor method (green), and a display method (pink).
ComponentSyntax PatternPurpose
Class declarationclass ClassName:Defines the blueprint; name uses CamelCase
Constructordef __init__(self, ...):Initializes attributes when an object is created
Instance attributeself.attr = valueStores data unique to each instance
Instance methoddef method(self, ...):A function that can read/modify the object's data
Calling a methodobj.method(args)Python passes obj as self automatically

Key syntax patterns for instance attributes and methods

SECTION 6

Worked Example — Building a BankAccount Class

Let's build a BankAccount class step by step. This example demonstrates how instance attributes store each account's unique balance, and how methods use self to deposit, withdraw, and check the balance.

Creating a BankAccount Class

  1. 1

    Step 1 — Define the Class and Constructor

    Start by writing the class header and the __init__ method. We need two instance attributes: self.owner and self.balance. We'll give balance a default value of 0.
    class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance
  2. 2

    Step 2 — Add a Deposit Method

    The deposit method takes self and an amount parameter. It adds the amount to self.balance. Notice that we use self.balance (not just balance) to modify the object's stored data.
    def deposit(self, amount): self.balance += amount return self.balance
  3. 3

    Step 3 — Add a Withdraw Method with Validation

    The withdraw method checks whether self.balance is large enough before subtracting. This shows how methods can use instance attributes to make decisions. If the balance is too low, we return a warning message instead.
    def withdraw(self, amount): if amount > self.balance: return 'Insufficient funds' self.balance -= amount return self.balance
  4. 4

    Step 4 — Create Instances and Test

    Now we create two separate accounts. Each has its own owner and balance. Depositing into one account does not affect the other — proving that instance attributes are independent.
    acct1 = BankAccount('Alice', 100) acct2 = BankAccount('Bob') acct1.deposit(50) → acct1.balance is 150 acct2.deposit(200) → acct2.balance is 200
  5. 5

    Step 5 — Verify Independence of Instances

    After the operations above, acct1.balance is 150 and acct2.balance is 200. Each object's self pointed to a different instance during each method call, so the data stayed separate.
    acct1.balance → 150 | acct2.balance → 200 (completely independent)

SECTION 7

Instance vs. Class Attributes — Key Differences

Python also supports class attributes — variables defined directly inside the class body (outside any method) that are shared by all instances. It's important to understand how these differ from instance attributes so you choose the right tool for the job.

FeatureInstance AttributeClass Attribute
Where definedInside __init__ using self.xDirectly in the class body
ScopeUnique to each objectShared across all objects
When createdEach time an instance is madeOnce, when the class is defined
Typical useUnique data like name, score, IDShared constants like species, school name
Exampleself.name = "Alice"species = "Canis lupus"

Instance attributes vs. class attributes

💡

WHEN TO USE WHICH

Use an instance attribute when each object should have its own version of the data (like each player's health points in a game). Use a class attribute when the value is the same for every object (like the maximum level cap in that same game). When in doubt, start with instance attributes — they're safer because changing one object won't accidentally affect another.

SECTION 8

Connection to Advanced OOP Concepts

Instance attributes and methods are the foundation of OOP, but Python offers more powerful features built on top of them. Understanding self and instance-level data prepares you for inheritance, encapsulation, and polymorphism — the three pillars of object-oriented design.

What You Know NowWhat's Coming Next
Instance attributes store per-object dataEncapsulation — using underscores to protect attributes from outside access
Instance methods use self to access dataInheritance — child classes inherit methods from parent classes and can override them
Each object has its own methodsPolymorphism — different classes can have methods with the same name that behave differently
Regular methods with selfClass & static methods — methods that use cls instead of self, or no special first parameter at all
Attributes accessed directlyProperties (@property) — methods that look like attributes, adding validation or computed values

How today's concepts connect to advanced OOP topics

Every advanced OOP pattern relies on the fact that self gives each object a persistent identity — a way to carry data and behavior together. Once you're comfortable creating classes with instance attributes and methods, you're ready to build complex, real-world programs that model anything from social media feeds to physics simulations.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why every instance method in Python needs self as its first parameter. What would happen if you forgot to include it?
Show answer
The self parameter is how a method knows which specific object it's working with. When you call obj.method(), Python automatically passes obj as the first argument, which the method receives as self. If you forget to include self in the method definition, Python will raise a TypeError because the method receives one more argument (the instance) than it has parameters to accept. Without self, the method also cannot access any instance attributes.

Question 2: BASIC

Write a class called Rectangle with instance attributes width and height, and an instance method area() that returns the area. Then create a rectangle with width 5 and height 3 and print its area.
Show answer
The class should look like this: class Rectangle: def __init__(self, width, height): self.width = width; self.height = height and then def area(self): return self.width * self.height. To test: r = Rectangle(5, 3) followed by print(r.area()) which outputs 15. The __init__ stores width and height on the instance, and area() uses self to access both attributes for the calculation.

Question 3: INTERMEDIATE

Create a Counter class with a count attribute starting at 0. Write three methods: increment() adds 1, decrement() subtracts 1 (but never below 0), and reset() sets count back to 0. Trace through this sequence and give the final count: c = Counter(); c.increment(); c.increment(); c.increment(); c.decrement(); c.increment().
Show answer
The Counter class: class Counter: def __init__(self): self.count = 0 with methods def increment(self): self.count += 1, def decrement(self): if self.count > 0: self.count -= 1, and def reset(self): self.count = 0. Tracing through: start at 0, increment three times to 3, decrement to 2, increment to 3. The final value of c.count is 3. Each method modifies self.count, which persists between method calls because it's stored on the instance.

Question 4: APPLIED

Design a Playlist class for a music app. It should store a name and a list of songs (initially empty). Include methods add_song(title), remove_song(title), and total_songs(). Explain why self.songs = [] should go in __init__ rather than as a default parameter like def __init__(self, name, songs=[]).
Show answer
The class: class Playlist: def __init__(self, name): self.name = name; self.songs = [] with def add_song(self, title): self.songs.append(title), def remove_song(self, title): if title in self.songs: self.songs.remove(title), and def total_songs(self): return len(self.songs). Using songs=[] as a default parameter is a famous Python gotcha: default mutable arguments are shared across all calls, meaning every Playlist would accidentally share the same list. By writing self.songs = [] inside __init__, each instance gets its own fresh list.

Question 5: CRITICAL THINKING

A student writes the following code and is confused about the output. The class Pet has __init__(self, name) setting self.name = name and self.tricks = []. There is a method def learn_trick(self, trick): self.tricks.append(trick) and another method def show_tricks(self): return self.tricks. The student runs: p1 = Pet('Max'); p2 = Pet('Bella'); p1.learn_trick('sit'); p1.learn_trick('shake'); p2.learn_trick('roll over'). What does p1.show_tricks() return? What does p2.show_tricks() return? Explain how self ensures the tricks stay separate.
Show answer
p1.show_tricks() returns ['sit', 'shake'] and p2.show_tricks() returns ['roll over']. When p1.learn_trick('sit') is called, Python translates it to Pet.learn_trick(p1, 'sit'), so self refers to p1 and self.tricks is p1's list. When p2.learn_trick('roll over') is called, self refers to p2, so it appends to a completely different list. Because self.tricks = [] is created inside __init__ for each instance, each Pet has its own independent tricks list.

SUMMARY

Lesson Summary

Summary

In this lesson, you learned that a class is a blueprint for creating objects, and an instance is a specific object built from that blueprint. Instance attributes are variables defined in the __init__ constructor using self.attribute = value, and each object gets its own independent copy. Instance methods are functions inside the class that take self as their first parameter, allowing them to read and modify the calling object's data.

The self parameter is the mechanism that connects each method call to the correct object — Python automatically passes the instance as the first argument when you write obj.method(). Remember: always include self in method definitions, always use self.x to access instance attributes, and understand that instance attributes are independent across objects. These fundamentals are the gateway to the full power of object-oriented programming in Python.

Varsity Tutors • Python • Instance Attributes & Methods