Create and Use Python Objects

Learn how to instantiate objects from a Python class, access their attributes with dot notation, call their methods, and manage multiple independent instances.

6 min read

You create Python objects from classes by calling the class name like a function, and putting those objects to work is where object-oriented programming stops being theory and becomes a practical tool. The syntax for creating an object is straightforward: you call the class name as if it were a function, pass any arguments the constructor expects, and receive back a fully initialized object. If you have been following this learning path in order, you have already performed this exact operation hundreds of times without realizing it. Every call to list() creates a new list object from the built-in list class. Every call to int() with a string argument creates a new integer object from the int class. Your own classes work the same way because Python treats user-defined classes and built-in types through a single unified object system. The only difference is that you wrote the blueprint yourself instead of having it provided by the language.

Once an object exists, everything you do with it flows through dot notation. You type the object's variable name, a dot, and then the name of the attribute you want to read or the method you want to call. The dot is Python's universal access operator for objects, and it works identically whether you are reading the length of a string or calling a deposit method on a bank account object you defined yourself. The consistency of dot notation across all object types is one of Python's design strengths: learn it once for strings and lists, and the same pattern applies to every class you will ever write or import from a library. The article on creating your first Python class introduced the class definition and the constructor method. This article picks up at the moment after the class is defined and shows you how objects live, interact, and travel through a real program.

The distinction between defining a class and using its objects is important enough to state explicitly. The class definition is written once and describes the shared structure: what attributes every object will have and what methods it can call. The objects you create from that class are the actual things your program manipulates at runtime. You might define a Student class in ten lines of code at the top of your file, then create thirty Student objects in a loop, each with a different name and ID. The class is the mold; the objects are the pieces that come out of it.

Instantiating objects: the constructor call

The technical term for creating an object from a class is instantiation, and the object that results is called an instance. When you write code that creates an object, you are instantiating the class. The word appears throughout Python's documentation and error messages, and it is worth internalizing because it helps you distinguish between the abstract idea of a class and the concrete reality of an object. A class is a type; an instance is a value of that type.

Instantiating an object looks exactly like calling a function because Python's class mechanism is built on top of its function-call mechanism. When Python encounters a class name followed by parentheses and arguments, it creates an empty object in memory, calls the initialization method with that object as the first argument and the provided values as the remaining arguments, and then returns the now-initialized object. You can assign the result to a variable, store it in a list, pass it directly to another function, or use it inline. The constructor call is an expression that evaluates to an object, which means it can appear anywhere an expression is allowed.

Here is a class that models a task in a to-do list, with a description, a priority level, and a completion status:

pythonpython
class Task:
    def __init__(self, description, priority):
        self.description = description
        self.priority = priority
        self.completed = False
 
    def mark_done(self):
        self.completed = True

The class definition is compact but complete. Every Task stores three attributes: a description string, a priority string, and a boolean tracking whether the task is finished. The mark_done method flips the completion flag to True when called. Now consider how objects created from this class behave independently:

pythonpython
task_one = Task("Write project report", "high")
task_two = Task("Review pull request", "medium")
 
task_one.mark_done()
print(task_one.completed)
print(task_two.completed)

The output is True for the first task and False for the second. Calling mark_done on task_one changed only that object's completed attribute. The second task remains untouched because each object carries its own independent copy of every instance attribute. This isolation is the essential property that makes objects useful: one definition, many independent instances, no cross-contamination.

Reading and writing object attributes

Attributes are the data an object carries, and you interact with them using the same dot notation that methods use. Reading an attribute is an expression that evaluates to the stored value. Writing an attribute is an assignment statement that replaces the existing value with a new one. Python does not distinguish between reading and writing syntax beyond the presence of an assignment operator, and the same dot notation works for both operations. This uniformity means you can treat an object's attributes the same way you treat variables, with the added structure that attributes are namespaced inside their object and cannot accidentally collide with attributes on other objects or with variables in the surrounding scope.

Attributes can hold any Python value. A string attribute stores text, an integer attribute stores a number, a list attribute stores a collection, and an attribute can even store another object, creating a chain of references that mirrors real-world relationships. A Library object might have a books attribute that holds a list of Book objects, and each Book object might have an author attribute that holds an Author object. This composition pattern is one of the most powerful techniques in object-oriented design, and it emerges naturally from treating objects as values that can be stored anywhere a primitive value can be stored.

Python does not restrict you to the attributes defined in the constructor. You can add a new attribute to an existing object at any time by assigning to a dot-notation name that does not yet exist on that object. While this flexibility is convenient for quick experiments, it is dangerous in larger programs. If different objects of the same class end up with different sets of attributes, code that expects a particular attribute to exist will fail with an error when it encounters an object that never received that attribute. The article on instance attributes in this section explores this topic in depth, including patterns for keeping attribute sets consistent across all instances.

Calling methods and understanding method dispatch

Calling a method on an object follows the same dot-notation pattern as accessing an attribute, with the addition of parentheses and any arguments the method requires beyond the automatic object reference. When you write a method call on an object, Python looks up the method name on the object's class, finds the method definition, and calls it with the object automatically inserted as the first argument. The parentheses are required because methods are callable. Forgetting them retrieves the method object itself without calling it, which is almost never what you want and produces no visible effect.

Methods can call other methods on the same object by using the special first parameter with dot notation inside the method body. A print_report method might call helper methods for calculating totals and formatting currency to break its work into smaller pieces without requiring the caller to orchestrate each step. This internal delegation keeps methods focused and readable, and it is one of the design patterns that experienced Python developers use to manage complexity inside larger classes.

Objects in collections and as function arguments

Objects are first-class values in Python, which means you can do anything with an object that you can do with an integer or a string. You can store objects in lists, sets, and dictionaries. You can pass objects as arguments to functions and return them as results. You can nest objects inside other objects to build composite structures that model complex real-world relationships. None of these operations require special syntax or additional configuration.

Storing objects in a list is the most common pattern for working with collections of similar things. A program that manages a to-do list might store Task objects in a list and then loop over the list to print summaries, filter by priority, or count completed items. The loop body reads each object's attributes through dot notation and calls its methods, again through dot notation. The code reads like a description of the problem rather than a description of the implementation, which is the goal that object-oriented design aims for.

Passing objects to functions is equally natural. A function that sends an email notification might accept a User object and read the user's email address and name from its attributes rather than requiring the caller to extract and pass those values separately. This pattern reduces the number of parameters functions need and makes the relationship between the function and the data it operates on explicit in the function signature. The article on Python functions covers parameter design in detail, and the patterns transfer directly to functions that work with objects.

Rune AI

Rune AI

Key Insights

  • Create an object by calling the class name followed by parentheses containing any arguments the init method expects.
  • Access object attributes with dot notation: object.attribute reads a value, and object.attribute = new_value writes one.
  • Call methods the same way: object.method(arguments), and Python automatically passes the object as the self parameter.
  • Every object is independent; changing one object's attributes never affects another object created from the same class.
  • Objects can be stored in lists, passed as function arguments, returned from functions, and nested inside other objects just like any other Python value.
RunePowered by Rune AI

Frequently Asked Questions

How many objects can I create from a single Python class?

There is no limit. You can create as many objects from a class as your program's memory allows. Each object is independent, with its own copy of every instance attribute, so creating a thousand objects from a Customer class gives you a thousand distinct customers, each with their own name, email, and order history.

What happens to an object after I create it and assign it to a variable?

The object lives in memory for as long as at least one variable or data structure references it. When there are no more references, Python's garbage collector automatically frees the memory. You do not need to manually delete objects in Python, which is one of the language's conveniences compared to languages like C++ where memory management is explicit.

Can I store Python objects inside lists and dictionaries?

Yes, and doing so is one of the most common patterns in object-oriented Python. You can store objects in any collection type, pass them as arguments to functions, return them from functions, and nest them inside other objects. A shopping cart object might contain a list of product objects, and a classroom object might contain a dictionary mapping student IDs to student objects.

Conclusion

Creating objects from classes and using them in the flow of your program is the practical payoff for learning class syntax. Once you can instantiate objects, access their attributes, call their methods, and pass them freely through your code, you have the core mechanical skills that every subsequent OOP topic depends on. Practice creating multiple objects, modifying their attributes, and storing them in collections until the dot notation feels as natural as calling a built-in function.