The article builds a straw man though. The "bad example" is bad because it introduces OOP for no reason at all.
What's wrong with:
def set_deadline(deadline):
if deadline <= datetime.now():
raise ValueError("Date must be in the future")
set_task_deadline = set_deadline
set_payment_deadline = set_deadline
You don't need code duplication to avoid bad abstractions.
Later down the line, if you want to have separate behaviour for task deadlines vs payment deadlines, you're going to have to go through your codebase and look at every call to set_deadline and figure out if it's being used to set a task deadline or payment deadline. If you have an inkling that the deadlines might need a different behaviour, the “good example” can save you an annoying refactor in the future.
Don’t make the symbol public, or call it _set_deadline, or whatever is the idiom in Python. The point of this example is ofc not having set_deadline be used, but the other symbols.
Again, you don’t need to duplicate a function body just to have semantic names.
Its not about OOP but the probability that those two functions will diverge. Linked elsewhere in the comments too, this article (https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction) is probably better at articulating the point.
What's wrong with:
You don't need code duplication to avoid bad abstractions.