Concepts / Understanding the self Parameter

Understanding the self Parameter

Constructors use the self parameter to initialize each new object and additional parameters to customize that object with specific data.

  • Programming
Interactive lab

Try it: Classes, Instances and Attributes

How a class creates instances with __init__ and self, why an attribute read checks the instance's __dict__ before the class, why assigning through an instance never changes the class, how a class-level list is shared by every instance, and why obj.method(x) is Class.method(obj, x).

How it works

  1. Dog(...) makes a new, empty instance and calls Dog.__init__(new_object, ...); self.name = ... stores into that object's __dict__.
  2. Reading obj.attr looks in obj.__dict__ first, then in the class.
  3. Assigning obj.attr = value always writes obj.__dict__, so it shadows a class attribute for that one object only.
  4. Mutating a class-level list (self.tricks.append) changes the one list every instance shares.
  5. obj.method is found on the class and bound to obj, so obj.method(x) runs Class.method(obj, x).

Default run (30 steps): Ready: class Dog and the statements you picked. Watch where each attribute is found and where each assignment writes. … Finished. Printed: ['sit'] / Wolf / Canis familiaris / Fido is 5.

Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Object numbers are illustrative, not real id() values.

Educational simulation

Loading the simulation…

One Blueprint, Many Guests

Imagine tracking guests at a party. Every guest needs a name and a count of how many times that guest has danced. A single class can define the structure for every guest, but Sally's name and dance count must remain separate from Jim's. The self parameter is what lets the constructor attach each value to the particular object currently being initialized.

Key point

The class provides a blueprint. Each object created from that class receives its own attribute data.

Connecting Parameters to Attributes

Inside a constructor, self refers to the particular object being initialized. In self.name = nam, nam is the incoming parameter, while self.name is the name attribute belonging to that particular object. The assignment takes the value received through nam and stores it in the object's name attribute. This makes self the bridge between a generic class definition and a concrete object.

new objectthe object passed as selfselfthat particular objectnameSallynamincoming valuebecomesowns attributeassigned by self.name = nam
How does the value passed to the constructor become data stored on one specific object?
Code walkthrough
class PartyAnimal:
    def __init__(self, nam):
        self.x = 0
        self.name = nam
        print(self.name, "constructed")
  1. 1
    The constructor receives the particular object through self and receives the supplied name through nam.
  2. 2
    self.x = 0 initializes the new object's x attribute.
  3. 3
    self.name = nam copies the incoming name value into that object's name attribute.
  4. 4
    The print statement displays the name stored on that object.
  • self identifies the particular object being initialized.
  • nam receives the value supplied when the object is created.
  • self.x = 0 gives every new object its own starting count.
  • self.name = nam stores the supplied name on that object.
Output
The constructor sets x to 0, stores the incoming name in name, and prints that name followed by constructed.

Creating Sally and Jim

Worked example

Two constructor calls

Trace what happens when two PartyAnimal objects are created with different names.

  1. 1
    Create s

    When s = PartyAnimal('Sally') runs, Python creates a new object and passes that object as self to __init__. The string Sally is passed as nam.

  2. 2
    Initialize s

    The constructor sets s's x attribute to 0 and sets s's name attribute to Sally. It then displays Sally constructed and binds the object to s.

  3. 3
    Create j

    When j = PartyAnimal('Jim') runs, Python creates another object and runs the constructor again. This time self refers to the second object and nam receives Jim.

  4. 4
    Initialize j

    The second constructor call sets j's x attribute to 0 and j's name attribute to Jim. It then displays Jim constructed and binds the second object to j.

Result

s and j refer to separate objects. Both begin with x equal to 0, but s has name Sally and j has name Jim.

PartyAnimal('Sally')__init__ for snam = Sallysname = Sally, x = 0PartyAnimal('Jim')__init__ for jnam = Jimjname = Jim, x = 0runsinitializesthenrunsinitializes
What happens step by step when two objects are created from the same class?

Why Counters Stay Independent

Each instance maintains its own copy of its attributes and occupies separate memory from other instances of the same class. Therefore, a method that increments s's x changes s's counter, not j's counter. If s.party() is called twice and j.party() is called once, s ends with x equal to 2 while j ends with x equal to 1. The shared class definition does not mean the objects share their attribute storage.

sname = Sallyx2x1jname = Jimownsowns
How can two objects from one class contain different values without changing each other?

What do you think happens?

Suppose s.party() is called, then j.party(), then s.party() again. What values should the two independent counters have?

  • s.x is 2 and j.x is 1

  • s.x is 1 and j.x is 1

  • s.x is 2 and j.x is 2

Reveal answer
Answer:

s.x is 2 and j.x is 1

The first and third calls operate on s, so s's counter is incremented twice. The middle call operates on j, so j's separate counter is incremented once.

Output
For this sequence:

s.party()
j.party()
s.party()

s.x ends at 2, and j.x ends at 1. The name attributes remain Sally and Jim.

Reading State Changes

python
Before
s = PartyAnimal("Sally")
j = PartyAnimal("Jim")

s: name = Sally, x = 0; j: name = Jim, x = 0

After
s.party()
j.party()
s.party()

s: name = Sally, x = 2; j: name = Jim, x = 1

The important tracing habit is to identify self at every method call. During s.party(), self refers to the Sally object. During j.party(), self refers to the Jim object. When s.party() runs again, self refers to the Sally object again, so its existing x value is increased rather than reset. The constructor runs when each object is created; later method calls operate on the already initialized instance.

Mistakes with self

  • Common mistake

    Treating nam and self.name as if they were the same thing.

    What it looks like

    nam receives the incoming value, while self.name is the attribute stored on the current object.

    Why it fails

    The parameter is the incoming value used during the constructor call. The self.name expression identifies the attribute belonging to the particular instance.

    Do this instead

    Read self.name = nam as: store the value from nam in this object's name attribute.

  • Common mistake

    Assuming that two objects created from one class share the same attribute values.

    What it looks like

    Expecting a change to s.x to change j.x.

    Why it fails

    Each instance has its own data storage and its own copies of its attributes.

    Do this instead

    Track the object represented by self in each call. Changes made through s affect s's attributes, while changes made through j affect j's attributes.

  • Common mistake

    Assuming the second constructor call reinitializes the first object.

    What it looks like

    Thinking that creating j after s resets s.x or changes s.name.

    Why it fails

    The second call creates a separate object and passes that different object as self.

    Do this instead

    Keep a separate state record for every instance created from the class.

Practice the Trace

MEDIUM

A class creates two objects, a = PartyAnimal("Ava") and b = PartyAnimal("Ben"). The constructor gives every new object x equal to 0 and stores the supplied name. Then the program calls a.party(), a.party(), and b.party(). What are the final values of a.name, a.x, b.name, and b.x? Explain which object self refers to during each call.

Hints
  • Record the initial state immediately after each constructor call.
  • The two calls on a affect the same object.
  • The call on b affects b's independent x attribute.
Worked example

Practice answer

Find the final state after a.party(), a.party(), and b.party().

  1. 1
    Initial state

    a has name Ava and x equal to 0. b has name Ben and x equal to 0.

  2. 2
    First call

    a.party() uses self for a, changing a.x from 0 to 1.

  3. 3
    Second call

    The next a.party() uses self for a again, changing a.x from 1 to 2.

  4. 4
    Third call

    b.party() uses self for b, changing b.x from 0 to 1. It does not change a.x.

Result

a.name is Ava, a.x is 2, b.name is Ben, and b.x is 1.

Key Takeaways

  1. 1
    self identifies the particular object being initialized or manipulated.
  2. 2
    A constructor parameter such as nam carries incoming data, while an assignment such as self.name = nam stores that data on the current object.
  3. 3
    Every new PartyAnimal begins with its own x value of 0 and receives its own name value.
  4. 4
    Instances created from the same class occupy separate memory and maintain independent attribute values.
  5. 5
    To predict a program's state, follow which object self refers to during each constructor or method call.

At a glance

  • self connects a constructor or method call to one particular object.
  • Assignments such as self.name = nam store incoming parameter values as attributes on that object.
  • Each instance has independent copies of its attributes, even when all instances come from the same class.
  • Tracking self at every call makes it possible to predict each object's final state and the program's output.

Related concepts

More concepts starting with U