Classes and Objects
A class is a blueprint; an object is a concrete thing built from it. In Python the keyword is one word, the body is an indented block, and the moment you define a class, Python executes it - creating a real object of type type that you can store, pass around, and call to make instances. Understanding that "a class is an object" is the single concept that makes the rest of Python OOP (metaclasses included) make sense, so this chapter builds it explicitly.
Defining and Using a Class
class Book:
"""A single item in the catalog."""
def __init__(self, title: str, author: str):
self.title = title
self.author = author
def summary(self) -> str:
return f"{self.title} by {self.author}"
b1 = Book("The Pragmatic Programmer", "Hunt & Thomas")
b2 = Book("Refactoring", "Martin Fowler")
print(b1.summary()) # The Pragmatic Programmer by Hunt & Thomas
print(b1.title) # attribute access
print(b1.summary()) # method call
print(type(b1)) # <class 'Book'>
print(isinstance(b1, Book)) # True
The call Book(...) does two things: it creates a new instance, and it calls the instance's __init__ to set it up (that is the next chapter's subject). After the call, b1 and b2 are two independent objects of the same class - same blueprint, separate data. type(b1) reports the class, and isinstance is the test you use at runtime, the foundation of the polymorphism chapters.
A Class Is an Object
class Book:
pass
print(type(Book)) # <class 'type'> - the class is an object
print(Book.__name__) # 'Book'
print(Book.__module__) # '__main__'
# classes can be stored, passed, and selected
catalog = {Book: []}
catalog[Book].append(Book("Any Title", "Anyone"))
print(len(catalog[Book])) # 1
# they can even carry their own attributes
Book.owner = "library-app"
print(Book.owner) # 'library-app' - belongs to the class itself
type(Book) is type - every class is an instance of type (or of a metaclass, chapter 21). That is why a class has __name__ and can hold attributes: it is just another Python object. Practical consequences: you can keep classes in dictionaries and lists, dispatch on them, and build small registries (a plugin pattern where each class registers itself by name). The metaclass chapter is simply "what if the type that makes classes needed to do more than the default."
Identity, Equality, State
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
a = Point(1, 2)
b = Point(1, 2)
c = a
print(a is c) # True - same object
print(a is b) # False - two objects
print(a == b) # False - default equality is identity
print(id(a) == id(c)) # True - id is the memory address
Every object has an identity (id, the memory address) and a default equality: == without __eq__ falls back to is. Two separately constructed Point(1, 2) objects are equal in position but not in identity - and Python says they are not equal until you teach the class __eq__ (operator overloading chapter). The distinction matters throughout: is for "the same thing," == for "the same value," and caching singletons (the intern pattern) is where the two deliberately coincide.
Naming and Documentation
- CapWords for classes (
BankAccount, notbankAccount) - PEP 8, and the signal that a name is a type. - One-liner docstring in every class body -
"""What it is and what it's for."""- it becomes__doc__and shows in help tools. - Pass as a body is legal (
class Marker: pass) for a placeholder, but a class without methods or data is a smell - it is a label, not an object. - Module layout: one primary class per module, or a small related family; the module docstring explains the domain, each class docstring explains its role.
Summary
Define with class Name:, instantiate by calling the class, and treat the result as an object with its own attributes and methods. Remember the load-bearing fact: the class itself is an object (of type type), which is why it can be stored, passed, and introspected - the premise the metaclass chapter builds on. Distinguish identity (is, id) from value equality (==, which defaults to identity until __eq__ is defined), follow the naming conventions, and give every class a one-line docstring. With that foundation, the next chapter opens the instance: __init__ and how an object gets its state.