A class packages data with the methods that act on it, turning a design into objects with recognizable state and behavior. This makes application functionality cleaner to use and easier to organize. section 4.1 section 4.2
Define a class as a template, then instantiate it whenever you need a concrete object. The __init__() method receives initial data, while self identifies the particular instance whose attributes and state a method uses. section 4.1.1
Representing people
A Person class can store first, middle, and last names and expose full_name() as its behavior. Creating several Person instances gives each person distinct state, even though all instances share the same class design. section 4.1.1
class Person:
def __init__(self, fname: str, mname: str = None, lname: str = None):
self.fname = fname
self.mname = mname
self.lname = lname
def full_name(self) -> str:
full_name = self.fname
if self.mname is not None:
full_name = f"{full_name} {self.mname}"
if self.lname is not None:
full_name = f"{full_name} {self.lname}"
return full_name
Inspect the progressive Person definition: __init__() assigns attributes, and full_name() combines available values into a result. section 4.1.1
Use classes to structure larger designs without forcing every relationship into inheritance:
- Treat class attributes and methods as public by default; Python relies on responsible use rather than protected or private access controls. section 4.1.1
- Use inheritance when a child genuinely extends a parent, and use composition when added functionality would otherwise create an awkward hierarchy. section 4.3
- Refactor related functions that operate on the same data into methods of a class when doing so improves readability and responsibility boundaries. section 3.2.5




















