A Class Wraps Up Data and What You Do With It
A class lets you define a type of thing — a user, a bank account, a configuration, a request — and everything that thing should be able to do. The thing is an instance of the class. The actions are called methods.
Here's the simplest possible class:
Breaking it down:
class User:starts a class definition. Class names use PascalCase by convention.__init__is the initializer. It runs automatically when you create a new instance. It sets up the instance's attributes usingself.whatever = ....greetis a method.selfis how a method refers to the instance it was called on.User("Rosa", "rosa@example.com")creates an instance. Python calls__init__withselfset to the new instance and the other arguments passed through.user.greet()calls the method. Python passesuserasselfautomatically.
self Is Just the First Argument
self isn't a keyword. It's the name Python calls the first parameter of instance methods — the instance the method is running on. You could call it anything; everyone uses self because everyone does.
Every instance method takes self as its first parameter. That's how a method knows which instance to read from or modify.
Adding Behavior
Methods can read and mutate state:
__repr__ is another special method — a "how do I show this object when printed?" hook. Defining a good one pays dividends during debugging.
Class Attributes vs Instance Attributes
Attributes defined in the class body (outside __init__) are shared across all instances. Attributes assigned to self are per-instance:
Use class attributes for genuinely shared values — constants, counters, config. Put instance-specific data in __init__.
Inheritance
A class can inherit from another, getting its methods and attributes for free and adding or overriding as needed:
super() gives you access to the parent class — commonly used inside __init__ to initialize the parent's attributes before adding your own:
Inheritance is useful but easy to overuse. A new Python programmer often reaches for inheritance when composition — holding an object as an attribute — would be clearer. Start with a flat class structure and only introduce inheritance when you have a real "is-a" relationship.
Dataclasses: A Nicer Default for Records
When you mostly want a class to hold a few attributes with some equality and representation behavior, @dataclass saves a lot of boilerplate:
Compare that to writing __init__, __repr__, and __eq__ by hand. Dataclasses are the right first choice when you'd otherwise write class Thing with nothing but __init__ setting attributes.
Properties: Computed Attributes
Sometimes you want an "attribute" that's actually computed. @property makes that look like attribute access:
Use properties when you'd write obj.get_something() — replacing it with a property makes the API feel more natural. Don't use them to hide expensive operations; a user expects attribute access to be fast.
Private by Convention
Python has no real private. By convention, attributes with a leading underscore are intended as private — readers are expected not to touch them from outside the class:
class Cache:
def __init__(self):
self._store = {}
def put(self, key, value):
self._store[key] = value
def get(self, key, default=None):
return self._store.get(key, default)
Double-underscore names (__name) trigger name mangling, which adds the class name to the attribute. That's a tool for avoiding collisions in inheritance, not a security measure.
When Not to Use a Class
If all you have is a bundle of related data, a dataclass or a plain dict or a named tuple will do fine. If all you have is one function, it doesn't need a class to hold it. Reach for classes when you have both state and behavior tied together — especially when you'll have many instances of the same kind.
A Final Example
A small file-like log buffer:
Notice __len__ — another magic method. Defining it makes len(logger) work, because len(x) is really a call to x.__len__(). Python has dozens of these hooks; you'll pick them up as needed.
Next: Generators
Classes tie data and behavior together. The next chapter looks at a different kind of abstraction — iteration — starting with generators: functions that produce values one at a time, pausing between them. They pair beautifully with the with-statement / context managers you'll meet right after.
Frequently Asked Questions
What is a class in Python?
A class is a blueprint for creating objects that bundle data (attributes) and behavior (methods) together. class User: defines a User class; User(...) creates an instance. Classes are Python's main tool for modeling things that have both state and behavior.
What does self mean in Python?
self is the first parameter of every instance method. When you call user.greet(), Python passes the user instance as self. Inside the method, self.name accesses that user's name attribute. The name self is a convention, not a keyword — but every Pythonista uses it.
Is Python object-oriented?
Yes — everything in Python is an object, including numbers, strings, and functions. You can do object-oriented programming when it fits, but Python doesn't force it. Many real-world Python programs use classes sparingly, preferring plain functions and data structures where possible.