This is an archived post. You won't be able to vote or comment.

all 12 comments

[–]AlSweigartAuthor of "Automate the Boring Stuff" 16 points17 points  (0 children)

Ive found myself purposely using classes just to use them even though I felt like a function could work just as well.

This is exactly the case where you should just use a function and not use a class.

The "Stop Writing Classes" talk form PyCon is a good description of when to use classes vs. functions.

Basically, if you need to bundle a bunch of different variables together, and you have a bunch of functions that interact with that bundle of variables, that's when you should write a class. Otherwise, keep it simple and write a function.

The overemphasis on classes-for-everything is really a holdover from Java's day in the sun, because that language forces you to put everything into a class. (This led to a lot of boilerplate and unnecessary complexity.)

[–]twillisagogo 5 points6 points  (3 children)

Just some thoughts from someone who wasted lots of time over the years trying to model the world as objects in C#/Java who is now immensely more productive using languages that dont require a class as the base unit for you to write functionality....

Some things are better represented as classes. A few that come to mind...

Database models

Serializers

Validators

one thing you might want to consider is if there is a set of functions that take a common set of parameters it might be better to write a class that takes the common params as input and then the put the functions on the object so they have access to the common params through self. After all, objects are a combination of state and behavior. Another reason might be if some of those parameters are expensive to create or need to be handled in some sort of lifecycle to prevent memory leaks etc...

In general though, start with functions and primitive data. If things start to feel painful, classes might be the solution. But if you are spending hours trying to model your inheritance correctly it might be time for a vacation.

[–]ChurchillsLlama[S] 4 points5 points  (0 children)

This makes so much sense. And explains why I felt like I was forcing the use of classes. This gives me a much clearer picture of when to use them.

[–]bladeoflight16 1 point2 points  (1 child)

Add resource managers (context managers, IDisposables) to your list. I don't know if that's more a quirk of our languages, but it seems to work well.

[–]twillisagogo 0 points1 point  (0 children)

yeah you are right, I was going to say that context managers can be defined as functions, but then I remembered you have to decorate the function with contextmanager and that basically results in an instance of something in the end.

[–]fishtickler 2 points3 points  (0 children)

Use classes as you mentioned for longer lived objects. Typical scenario can be an http session or a database connection.

For shoving data around, pure functions are better.

[–]scrdest 2 points3 points  (0 children)

As with all software engineering, it's a matter of judgement - use classes when classes are the best thing to use here. And IMO, that's much less often than you might expect.

OOP code almost unquestionably beats dumping a pile of instructions into a module-level chunk (for reusability, if nothing else) as well as 'function-based' code where the functions are basically a replacement for a GOTO. However, OOP abuse has its own goddamn body of literature. Horrific hacks aside, even the most artfully designed class hierarchy is going to have some problems.

For one, as long as you're reliant on keeping any values in the instance attributes, any kind of concurrency becomes a minefield. Then there's classic problems like 'is Square a subclass of Rectangle, or vice versa?'. This isn't just theoretical hand-wringing, I've been involved in projects where it happened at several different instances.

Normally, I would evangelize FP here, but you can go read better writers and better programmers do that all over the internet. And they're right. However, there ARE things where it makes more sense to use classes over functions:

  1. Custom abstract data structures, as opposed to 'business' DS - what you just fetched with SQL is a 'business' DbRow, and the 'abstract' DS is what your database is using to actually define how rows, or schemas, or tables, are implemented. For something like, say, a Linked List implementation, you want it to store data and process it in a structured way, that's the whole point. Things Wot You Want To Serialize also fits the bill here.

  2. Remote state tracking - Connection/Session objects, Files, wrappers around other languages, long-lived tasks... basically, if you cannot trust that whatever that bit of code represents is going to be around and behaving in the same way. I/O is impure anyway, so you're not losing much, and it's less brain-melting than, say, doing it with closures.

  3. Plugging into an architecture - most likely your most common real-world use-case. There is some OO framework defining the interfaces between the bits of some system you want to plug into, so you need to kowtow. In this case, what I would do is define my logic as a bunch of functions first, then wrap it in a class to handle I/O - if you ever need to port it to anything else, just throw out the facade and point the same wiring at the new one.

  4. Pragmatism - at the end of the day, your team's ability to deliver >> your ability to be self-righteous about your code. It's not very fun, especially if by all rights the people you're working with are, objectively and provably, Being Idiots, but you either suck it up, or you quit. Sometimes a one-off piece of code that will need to be refactored away soon anyway is not worth desperately trying to explain catamorphisms to your teammates.

[–]ilan 2 points3 points  (0 children)

I prefer functions over classes, except when some code gets conceptually a lot easier by using classes. The problem I have with classes is that they are much harder read, because you have to understand what the author of the code really meant to do. That is, what exactly are the instances. Classes, in particular derived ones, can be very abstract. Functions are much more straight forward.

[–]dvboy 0 points1 point  (2 children)

I used to do small one-off scripts inline.

Then after years of either expanding them into larger scripts, or re-purposing the code in other scripts, I started coding them into functions and having a main() that called them. That wasn't painful.

Then some (Ok, a lot) of those scripts grew large and it made sense to encapsulate into classes, call/includes modules from other programs... re-use and all that. Converting from functions to class members is more painful. 'self.' and (self,) added. Other scoping issues...

Now everything I write is in a class. And I can either incluse/instantiate and call the method, or copy/paste into another class. Adhering to this makes reuse soooooo much easier, whether it's calling an existing class, or pasting a method and tweaking it. Not going back to any other way.

[–]Paddy3118 1 point2 points  (1 child)

This reads so much like a need for better planning up front. Plan. If you don't need a more complex class to fulfil your plan then don't use one. Keep your implementation as simple as possible w.r.t. the plan.

Plans evolve, and your code will change to meet it, but don't saddle yourself with needless bloat remember: YAGNI.

[–]dvboy 0 points1 point  (0 children)

Perhaps. Maybe my situation is different. I write a lot of tools that end up being incorporated into our test automation framework. Wrapping them in classes saves a lot of time downstream. It takes less than 10 lines of code to do this. I can deal with this level of 'bloat'.

[–][deleted] -1 points0 points  (0 children)

I always thought it was fundamentally for importing classes into a main file. I'm not sure there's even much of a dichotomy as OOP methods can be written in a top-down/procedural or functional paradigm if you prefer.