6 things you’re missing out on by never using classes in your Python code
Maybe you’ve been using Python for a while now and you’re starting to feel like you’re getting the hang of it. But one day you catch yourself thinking: “What about classes?”
You see other developers at your company or online discussing the merits of object oriented programming. “Classes are incredibly useful and everyone should know about them,” they say.
You’re beginning to get worried about writing your code the right way:
“I’ve never defined a class… I mean I know how to get stuff done quickly. I use functions and modules for everything, often many small functions whose output I feed into another. I simply don’t use classes because I never felt the need for them.
What am I really missing out on?
Am I shorting myself on something by never using classes in my Python code?“
I noticed that this is a common question for Python developers with a (data) science or numerical computing background.
You may be familiar with languages like R or Matlab and you haven’t really felt the need to use classes in your day to day work writing Python code. Most of your computations are sequential in nature and you found that a structured programming style worked just fine for the category of analytical problems you’re facing.
But still … there’s this nagging feeling in the back of your head – “What am I really missing out on by not writing classes?”
Let me try and help you out there. In this post I’m going over 6 things you might be missing out on by never using classes in your Python code. I’m not an OOP zealot by any means but I figured a post like that would be helpful to some folks in the Python community.
Alright, let’s dive in!
1. Easier collections of fields
If you categorically avoid the use of classes it’s easy to find yourself in a situation where you just re-invent your own “ad-hoc classes” using other built-in data structures, like lists or dicts.
For example, you might end up with lots of lists or dicts that share the same keys to access different kinds of data associated with a single logical object:
car_colors = 'yellow' # Color of Car 23 car_mileage = 38189.4 # Mileage of Car 23
By switching to classes you could have a single list of objects, each of which has several named fields on it to address the associated data:
cars.color = 'yellow' cars.mileage = 38189.4
Instead of using lists and dictionaries that happen to contain all your data you can keep all of it under one roof, which makes accessing and passing these objects around much more convenient.
You’ll also no longer need to pass around big tuples of stuff from function to function.
(Side note: You may want to go and learn more about Python’s
namedtuple objects which reduce the amount of boilerplate class-code you need to write if you just want simple C-style records of fields. Notice that
namedtuple objects are implemented as Python classes internally.)
2. Simpler domain models
It’s easier to think about a domain model with a number of classes that have some relation to each other, than it is to think about lists and dictionaries that are linked together by shared keys and indexes.
Some domains lend themselves well to being modeled with classes, for example GUI controls. I found that with an object-oriented approach the domain model sometimes becomes easier to discuss and reason about with other developers. And that’s always a good thing.
3. The ability to chain objects together and let them interact in an expressive way
This is where object oriented programming really shines – when the objects you’re dealing with have behavior on them. For example, a button that can be clicked or a car that can accelerate and brake.
In this case it helps if you encapsulate those behaviors by making methods on your Button and Car classes so that other objects can call those methods and change the Button’s or Car’s internal state without knowing how that operation is implemented.
Especially when you have a lot of behavior on your objects it’s helpful to have it all in the same place and under one roof that is the object itself. That way you can chain objects together and let them interact in an expressive way that’s hard to emulate in a procedural coding style.
For an example of this compare the following two code samples implementing the same sequence of interactions.
The OOP version:
if not garage.is_full: garage.add(my_car) my_car.turn_off() garage.close()
vs the non-OOP / procedural version:
if not is_garage_full(garage): add_car_to_garage(my_car, garage) turn_off_car(my_car) close_garage(garage)
4. Custom exceptions
If you want to define custom exception types in Python there isn’t really a way around using classes. Custom exception help communicate the programmer’s intent more clearly and they’re a great debugging aid.
Here’s an example of a more specific exception type that’s based on one of Python’s built-in exceptions:
class ValueTooSmallError(ValueError): pass
I’ve recorded a quick screencast tutorial that shows you how to use this technique to make your code more readable:
» Subscribe to the dbader.org YouTube Channel for more Python tutorials.
5. Fitting in with the coding style used by your colleagues
Another good reason for learning about OOP is that it is still a popular paradigm in the programming community. Therefore it is likely that you’ll be working with an object-oriented code base at some point in your career.
It can be difficult to integrate and maintain code in a single code base that was written in a different style, let’s say functional vs object-oriented.
If you’d rather avoid being the one engineer sticking out with an idiosyncratic coding style then it might be worth familiarizing yourself with OOP and classes.
6. Useful OOP design patterns
There’s a large body of OOP design patterns out there that can speed up development and improve readability. Design patterns are not a panacea and as with all things they can be overused – but in some situations being able to apply common design patterns will be helpful.
I’m going to shamelessly plug one of my open-source Python modules, schedule, as an example here. It uses the Builder pattern to let you schedule periodic jobs with a developer-friendly API:
import schedule def job(): print("I'm working...") schedule.every(10).minutes.do(job) schedule.every().hour.do(job) schedule.every().day.at("10:30").do(job)