Imagine you need to fill a list with square numbers - or more complex objects, but we'll just use square numbers to keep it simple. Here's one way to do it:
MAX_ROOT = 100
values = []
for x in range(MAX_ROOT):
values.append(x**2)
print(values)
Does the job, and much easier to follow than the C version:
#include <stdio.h>
#define MAX_VALUE 100
int main() {
int values[MAX_VALUE];
for (int i=0; i < MAX_VALUE; i++) {
values[i] = i * i;
}
for (int i=0; i < MAX_VALUE; i++) {
printf("%d %d\n", i, values[i]);
}
return 0;
}
What makes it easier to follow? Several things, which I would summarize as: "the Python code is higher level".
'Higher Level' means it abstracts over details of HOW to accomplish what you want... so you focus more on specifying WHAT you want, and the language figures out how to make it happen.
But we can go higher level than this. For example, a list comprehension:
values = [ x**2 for x in range(MAX_ROOT) ]
This is more 'declarative', which is one way to make a language higher level. You design the language so you can declare what you want, rather than spell out how to assemble it.
But what if you want to create your own high-level, declarative syntax? Not a list comprehension, but something else entirely?
In fact, Python provides a way to do this:
OOP.
This may be surprising. You know the benefits of OOP already, such as:
- Code becomes more reusable
- More organized, so your code is easier to reason about
- More encapsulated, thus de-inter-twining the logic
- Code is more testable
But there is one underappreciated benefit of OOP. Which is that - when you do it right - you can make your code more high level.
For example, this class that represents a range of dates:
class DateRange:
def __init__(self, start, end):
if end < start:
raise ValueError(f"End date {end} must be after start date {start}")
self.start = start
self.end = end
def __contains__(self, when):
return self.start <= when <= self.end
def __len__(self):
return 1 + (self.end - self.start).days
Use it thusly:
>>> from datetime import date
>>> date_range = DateRange(date(2050, 3, 1), date(2050, 3, 15))
>>> len(date_range)
15
>>> date(2050, 3, 10) in date_range
True
>>> date(2050, 7, 1) in date_range
False
Do you see how this abstraction called DateRange is higher level than mucking about with datetime.date() objects? That it is cleaner, which means it is easier to reason about; and common operations are encapsulated in simple methods, some of which even merge with the baked-in Python syntax.
You can extend these ideas further, for example to create domain-specific languages. Like the filtering in square brackets that Pandas provides for DataFrames:
import pandas as pd
df = pd.read_csv('/path/to/big/file.csv')
df[df.A + df.B == 19]
df[d.B - d.C >= 3]
df[(df.A > 0) & (df.B >= 12)]
df[(df.A >= 3) | (df.B == 11)]
And even if you have been writing classes in Python for years...
Consider there may be more to OOP than you realize. Especially when writing high-quality, engineering-grade code you are proud of.
If you liked this, you will enjoy the Powerful Python Newsletter.
Top comments (0)