I wish python had a clean way to define types without defining classes. Think a _good_ mechanism to define the shape of data contained within primitives/builtins containers without classes -- ala json/typescript (ideally extended with ndarray and a sane way to represent time)
Python classes wrapped around the actual "data" are sometimes necessary but generally always bad boilerplate in my experience.
You can use classes withput doing full blown OO. Classes are not only for encapsulating data with behavior or code reuse through inheritance hierachies. Sure, you can use them for that, but you can also use them for code as documentation.
IMHO using adhoc data structures like dicts and named tuples is schemaless programming - the equivalent of choosing MongoDB over a SQL database, but without any of the performance arguments. It's fine for small one off scripts, but as your software's complexity grows the implicit (and often entirely undocumented) schema turns into tech debt.
> Managing State with Simple Structures: Use Dictionaries or Lists
This is bad advice because you wind up hardcoding (or using vars for your key names, slightly better) your behavior on the state of the dict. Seriously, why not just make a class? You get better IDE and possibly better LLM support.
Since this seems to be a guide for programmers new to Python, I wish it made it clear that dataclasses are classes; @dataclass just provides a concise way of defining classes with default functionality for instance construction, equality, hash, and string representation.
Otherwise, this is a decent set of reminders for programmers coming from more class-oriented languages.
Wow, that is an impressively uninsightful article.
Apart from the first option, all of those are really obviously not classes.
The first option is a suggestion to use named tuples or dataclasses. That is sensible, but you are using a class in that case – those are just helpers for making them. You can even add your own methods to dataclasses.
For named tuples, you are better off using the typed version typing.NamedTuple [1] instead of the classical one suggested in the article. Aside from providing typing, the typed version has a much nicer syntax to define it and lets you add methods (like dataclasses but unlike classical named tuples)
I agree grouping behaviors (functions) doesn't require classes, but since Python does not have structs, classes are the only way to provide types (or at least type hints) for non-scalar data. Dicts can only have a key type and value types, while lists/tuples can only carry one type.
class Person:
age: int
name: str
Would have to be boiled down to `dict[str, Any]` (or worse, `tuple[Any]`). You could use `typing.NamedTuple` without defining a class (`NamedTuple('Person', [('name', str), ('age', int)])`) - but subjectively, this is much less readable than simply using a data class.
I fail to see any argument why the namedtuple beats the usage of a class here. A class in a development view of the problem, is also a structure to build and expand upon, a tuple is a promise this won't be expanded upon. Clearly case dependent which is better.
Sure, some people use classes when all they need is a namespace - and for that having a file is enough.
If there is a collection of related data, it is (often) good to have it as a class. In the config example, a class may work better - as then you can pass it as an argument somewhere.
Classes aren't outright bad, but I've seen them misused so many times that I knee-jerk cringe when I see the keyword `class` in any programming language; too often have I seen classes misused and misapplied (in my opinion).
If what you're writing is fundamentally a singular self-described action, there's almost certainly no reason to apply thinginess to it. But I see this practice all the time in open-source code, and it describes Java to a tee.
Is the mutation of your data best described as a behavior? If not, then a simpler data structure and functions that act upon that structure may be easier to understand and be more composable in the long run.
And almost never use inheritance, especially if that inheritance chain goes beyond one ancestor.
I almost never use classes these days. If I were writing a game, I imagine that classes would be conceptually helpful in making sense of ideas that manifest as things with behavior in a virtual world. For general programming tasks, I find them largely unhelpful and to be a potential trap.
EDIT: When I said I "almost never" use classes, of course I do work with classes at my job because it would be obnoxious of me to deviate from the collective pattern. With my own personal code, the only time I end up using classes is if I'm forced to (ex. web components).
Data classes and enums are the move, IMO, in python, to address the considerations of the article. Enums in particular have room for improvement, but I think they're good enough. (To use in place of common python patterns like strings for choices, or sets of booleans)
Python is neither object-oriented nor functional. It’s pythonic. It was popularized by academia because it’s good for beginner programming assignments, and unfortunately the legacy of object-oriented Python in academia forced ML to inherit it.
For people who think classes are terrible, can you explain how you would implement the functionality associated with a Pandas DataFrame, without using classes? Did the Pandas developers simply get it wrong when choosing how to build this?
The examples provided here cant be real, right? Like look at the MathUtils one. No one is so braindead that they would add 2 redundant lines on top of their function def right?
This isnt the same thing. This is leveraging inheritance. The mathutils example isnt. If the example is supposed to include cases where inheritance is being used and the parent classes methods are important to the child class functioning, then the point in the op is not as valid anymore
I’m relative newbie to Python (come from Javascript) but I’ve been building a Flask web app for the last couple of months. Almost my entire Flask codebase is just packages with functions. There are only a handful of classes and that’s because I am overriding some Exception classes with custom behavior.
There just comes a point where they make sense. The article uses some very minimal examples, but the point at which I find myself reaching for classes is the point at which it's gotten complex enough to need them.
I find them useful when my code has become a big, sprawling mess of functions throwing dicts around, and I'm losing track of what the structure should be.
They're useful when they make sense. I can't think of many projects I've started off with them, but they've helped me straighten a few out.
Neither love them nor hate them, and struggle to see why folks get animated about them. They're a useful tool, sometimes.
This is kind of a poor argument. Its examples are unrealistic, so it’s hard to take the author seriously. It doesn’t delve deep into things like the risks of a strict inheritance hierarchy vs. the benefits of functional composition, or more interesting things like the subtleties of using module-scoped state versus function or class-scoped state. The list goes on.
Instead of a slightly dogmatic and mostly unsubstantiated stance, I find less opinionated guidance on what to do when much more helpful. After all the language has all of these constructs for a reason.
While I may see a strong argument for a lighter-weight programming model, it is not laid out in this article.
Apart from dataclasses (which generally should have slots and/or be frozen), this article gives way too much inapplicable advice, which is not scalable even in a small sized project.
One of the things that annoys me about a lot of “modern” Python code is that is reads like Java cosplay: entire classes that are nothing but properties, “helpers” for iteration that make a shambles of ordinary for loops, etc. I have always preferred clean modules and a functional style with well defined signatures, and find it more maintainable to boot.
26 comments
[ 0.18 ms ] story [ 1141 ms ] threadI wish python had a clean way to define types without defining classes. Think a _good_ mechanism to define the shape of data contained within primitives/builtins containers without classes -- ala json/typescript (ideally extended with ndarray and a sane way to represent time)
Python classes wrapped around the actual "data" are sometimes necessary but generally always bad boilerplate in my experience.
IMHO using adhoc data structures like dicts and named tuples is schemaless programming - the equivalent of choosing MongoDB over a SQL database, but without any of the performance arguments. It's fine for small one off scripts, but as your software's complexity grows the implicit (and often entirely undocumented) schema turns into tech debt.
This is bad advice because you wind up hardcoding (or using vars for your key names, slightly better) your behavior on the state of the dict. Seriously, why not just make a class? You get better IDE and possibly better LLM support.
[1] - https://www.youtube.com/watch?v=o9pEzgHorH0
Otherwise, this is a decent set of reminders for programmers coming from more class-oriented languages.
Apart from the first option, all of those are really obviously not classes.
The first option is a suggestion to use named tuples or dataclasses. That is sensible, but you are using a class in that case – those are just helpers for making them. You can even add your own methods to dataclasses.
For named tuples, you are better off using the typed version typing.NamedTuple [1] instead of the classical one suggested in the article. Aside from providing typing, the typed version has a much nicer syntax to define it and lets you add methods (like dataclasses but unlike classical named tuples)
[1] https://docs.python.org/3/library/typing.html#typing.NamedTu...
If there is a collection of related data, it is (often) good to have it as a class. In the config example, a class may work better - as then you can pass it as an argument somewhere.
At the same time, in many cases, instead of a dataclass, it is worth considering Pydantic (https://docs.pydantic.dev/latest/concepts/dataclasses/). It can deal with validation when data comes from external sources.
If what you're writing is fundamentally a singular self-described action, there's almost certainly no reason to apply thinginess to it. But I see this practice all the time in open-source code, and it describes Java to a tee.
Is the mutation of your data best described as a behavior? If not, then a simpler data structure and functions that act upon that structure may be easier to understand and be more composable in the long run.
And almost never use inheritance, especially if that inheritance chain goes beyond one ancestor.
I almost never use classes these days. If I were writing a game, I imagine that classes would be conceptually helpful in making sense of ideas that manifest as things with behavior in a virtual world. For general programming tasks, I find them largely unhelpful and to be a potential trap.
EDIT: When I said I "almost never" use classes, of course I do work with classes at my job because it would be obnoxious of me to deviate from the collective pattern. With my own personal code, the only time I end up using classes is if I'm forced to (ex. web components).
If you find yourself implementing a singleton in Python, you probably wanted a module.
https://pandas.pydata.org/docs/reference/api/pandas.DataFram...
Apparently Grant Sanderson (3Blue1Brown), the creator of Manim, is braindead:
I find them useful when my code has become a big, sprawling mess of functions throwing dicts around, and I'm losing track of what the structure should be.
They're useful when they make sense. I can't think of many projects I've started off with them, but they've helped me straighten a few out.
Neither love them nor hate them, and struggle to see why folks get animated about them. They're a useful tool, sometimes.
Instead of a slightly dogmatic and mostly unsubstantiated stance, I find less opinionated guidance on what to do when much more helpful. After all the language has all of these constructs for a reason.
While I may see a strong argument for a lighter-weight programming model, it is not laid out in this article.