Concepts / Methods and Object State

Methods and Object State

Placing setup in the constructor means callers do not repeat setup steps for every object they create.

  • 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…

A Bank Account Needs a Starting Point

When you build a class, each object you create usually needs some starting information before it can be used. A bank account needs an owner and a starting balance. A game character needs a name and a health value. The work of giving a new object its starting information is called initialization.

This article follows one question through the rest of the lesson: when an object first comes into existence, who is responsible for making sure it starts in a usable state, and how can the code be arranged so that this responsibility is handled reliably? The tool for that job is the constructor. In Python, a constructor is written as a method named __init__.

Tracing an Account From Creation to First Use

Follow one account from the moment the caller asks for it to the moment its first method runs. Read the diagram from top to bottom. Each arrow is one step, and the order matters: the constructor runs during creation, while ordinary methods run only after the object already exists.

CallerAccount classNew account object__init__ constructorAccount("Ada", 100)create new objectcall __init__assign owner and balancereturn ready objectshow()
In what order do the steps happen from creating an account to its first method call?
  1. 1
    The caller writes Account("Ada", 100).
  2. 2
    A new object is created from the Account class.
  3. 3
    The constructor runs on that new object and assigns its owner and balance fields.
  4. 4
    The finished object is handed back to the caller.
  5. 5
    The caller makes its first method call, show(), which works because the fields already exist.

The important point is step three. The caller never has to run a separate setup line before show() becomes safe to call, because the class performed the setup while the object was being created.

What a Constructor Is For

Definition

A constructor is used to initialize classes and exists to guarantee and simplify that initialization.

The word simplify means the person who creates an object does not have to know every step needed to prepare it. The class takes on that work. A caller writes one creation line and receives an object that is already prepared.

The word guarantee means the initialization does not depend on the caller remembering to do it. If the constructor is the place where a class sets up its starting state, then every object built from that class goes through the same setup. Fields that methods depend on should be assigned in the constructor, so every object starts in a usable state.

A constructor is one kind of method among the methods a class can contain, but its job is different from the others. The constructor prepares the object during creation. Ordinary methods operate on an object after it exists.

Key point

Placing setup in the constructor means callers do not repeat setup steps for every object they create.

Two Ways to Set Up an Account

Here are two versions of the same small Account class. Both produce an account owned by Ada with a balance of 100. The first version leaves setup to the caller through a separate setup method. The second version places the same assignments inside the constructor.

python
Output (expected)
Ada has 100
Notes on the code
  • setup is an ordinary method, so the caller must remember to call it
  • Account() takes no arguments because the class itself does no setup
  • The second line after creation is the setup step every caller must write
python
Output (expected)
Ada has 100
Notes on the code
  • __init__ is the constructor, written as a method named __init__
  • The same two assignments now run once, inside the class
  • The caller writes one creation line and no separate setup line
Worked example

Tracing Account("Ada", 100) through the constructor

What state does the object hold after Account("Ada", 100) runs, and what does show() return?

  1. 1
    Create the object

    The class produces a new account object from the Account("Ada", 100) call.

  2. 2
    Run the constructor

    __init__ receives owner set to "Ada" and balance set to 100.

  3. 3
    Assign the fields

    self.owner becomes "Ada" and self.balance becomes 100.

  4. 4
    Call show()

    show() reads self.owner and self.balance, which both exist, and builds the text from them.

Result

The object holds owner "Ada" and balance 100, and show() returns "Ada has 100".

Account("Ada", 100)Account()__init__ assignsfieldssetup("Ada", 100)Ready accountReady accountruns during creationcaller must call
How do the code paths differ when a constructor initializes the object versus when the caller must call a separate setup method?

The two paths reach the same result, but they differ in where the setup lives. In the separate setup path, every caller must write the second line, and each place that creates an object would have to repeat the same steps. In the constructor path, the steps are written once inside the class, and every object built from it goes through the same setup.

How an Account's State Changes at Creation

An object's state is the set of values its fields hold at a given moment. Follow the state of the account from creation onward. A new object exists before any field has been assigned. The constructor then assigns the owner and balance, and the object moves into a state where its methods can rely on those fields.

New objectno fields assigned__init__ runsowner Ada, balance100usable stateshow() calledcreationassign fieldsfirst use
What state does an object hold right after creation, and how does the constructor move it into a valid, usable state?

Notice that the first state is not a usable one. The constructor is what moves the object from having no starting information to holding a complete set of fields. Because that move happens during creation, the object is already in its usable state by the time the caller holds it.

Mistakes When Setup Is Left to the Caller

When initialization is left to the caller, the object can exist in a state where a method runs before the fields it depends on have been assigned. The diagram contrasts the two arrangements. In the first, the caller creates the account and uses it without setup. In the second, the constructor has already prepared the object.

Account()Account("Ada", 100)no setup callfields set in__init__show() used earlyAda has 100
What goes wrong when the caller forgets a setup call, and how does the object look when a method is used too early?
  • Common mistake

    Creating the object and using it without the setup step

    What it looks like
    acct = Account() followed by print(acct.show()), in a class whose show() method reads self.owner and self.balance
    Why it fails

    No code assigned owner or balance, so show() has nothing valid to read and the object is not in a usable state.

    Do this instead

    Assign the fields in __init__ so that Account("Ada", 100) sets them during creation.

  • Common mistake

    Repeating the same setup lines wherever an object is created

    What it looks like

    Writing acct.setup("Ada", 100) after every Account() call in a large program

    Why it fails

    Each creation site must remember the steps, so one forgotten call leaves that object unprepared.

    Do this instead

    Move the assignments into the constructor so every object built from the class goes through the same setup.

  • Common mistake

    Assigning only some of the fields that methods depend on

    Why it fails

    A method that reads a field the constructor never assigned is a method that can be called on a not-yet-usable object.

    Do this instead

    Assign every field that methods depend on in the constructor.

A method that reads a field the constructor never assigned can be called on an object that is not yet usable. Check that every field your methods rely on is assigned in the constructor.

Predicting the Starting State

What do you think happens?

Using the constructor version of Account from earlier, what does acct.show() return after acct = Account("Grace", 50)?

Reveal answer
Answer:

Grace has 50

The constructor assigns owner to "Grace" and balance to 50 during creation, so show() builds its text from those two fields.

EASY

A class has a method named reset() that assigns self.owner and self.balance, and the caller must call reset() after creating each object. Rewrite the class so the same assignments happen in __init__. Which lines move, and what extra line can the caller now drop?

Hints
  • Look for the assignment lines inside reset().
  • After the move, the caller no longer needs a second line after the creation line.

Key Takeaways

  • A constructor exists to guarantee and simplify class initialization: the class does the setup, so callers do not have to repeat it for every object.
  • In Python the constructor is written as a method named __init__, and it runs during creation, before the caller receives the object.
  • Ordinary methods operate on an object after it exists, so the fields they depend on should be assigned in the constructor.
  • When setup is left to the caller, a method can be used before its fields are assigned, and each creation site must remember the same steps.

Related concepts

More concepts starting with M