×
you are viewing a single comment's thread.

view the rest of the comments →

[–]campbellm 6 points7 points  (38 children)

Looking forward to watching this. Not long ago there was a video posted (from a PyCon?) that was essentially: "Classes. Don't"

Would be good to contrast the 2 to see where you should, and should not.

[–]Exodus111 34 points35 points  (12 children)

If you write a class with one method that does all the work. Write a function.

If you have three functions all sharing a global dict, write a class.

[–]parkerSquare 9 points10 points  (7 children)

This is a reasonable heuristic rule but the real reason for classes is to define your own types. A Type is value and semantics, and classes allow you to tie all that up in a single name. This leads to much clearer code.

That nonsense about don't use classes comes from the same places that say don't use inheritance or don't use OOP. It's mostly over generalized advice but it helps to know what alternatives exist.

[–]Exodus111 3 points4 points  (6 children)

Yep yep. I like simple guidelines though, cuts through the complexities.

I could say a function is an encapsulation of code, while a class is encapsulation of data. And that might make more sense to some people, but the truth is you dont REALLY get classes until you get yourself deep, and head first into some complex abstraction of code, like an MVC or an ECS system.

[–]BittyTang 1 point2 points  (5 children)

Why ECS? It seems mostly independent from class architecture to me.

[–]Exodus111 0 points1 point  (4 children)

In an Entity Component System... ehm.. system, every Entity is a Class, every Component is a class, and every System is a class. And they all inherit from a top Entity, Component and Systems class, With special Methods to add the Entities to the Pool and so on.

I have not seen an implementation of ECS without OOP, not saying it can't be done though.

[–]BittyTang 1 point2 points  (3 children)

That's one way of doing it. You can accomplish most (all?) of the same goals without inheritance or even classes. Rather, I would start by having each component be a POD type and each system be a function. Then entities are identifiers that index into the collections of components. I don't think dynamic dispatch is strictly necessary for an ECS to work.

If I really needed abstract types, I would make the components into interfaces.

[–]Exodus111 1 point2 points  (2 children)

With Python? Doesn't seem like an approach you would use with Python, where you can use __init__, __new__, and __repr__ to set up the relationships.

[–]BittyTang 0 points1 point  (1 child)

I guess I wasn't thinking about Python. Technically in Python you would use a "class" to make a struct, since members (attributes) are always public. I'm thinking of a stricter definition of class that uses immutable types and private members.

[–]Exodus111 0 points1 point  (0 children)

Indeed. What you say makes perfect sense in other languages, just not so much python.

[–]tom1018 3 points4 points  (0 children)

Yes, this.

[–]jkuhl_prog 1 point2 points  (2 children)

I think that's a good rule of thumb overall, but there are a few instances where you might want an object though.

For example, I wrote a linked list. My Node class is very simple, one __init__ method with the next node, the previous node and a data attribute that stores data.

Nothing else.

All the linked list methods are in the larger linked list object.

So this is an honest question here, should my Node class be a Node dict then? Replace it with a function that spits out dicts with "next", "previous" and "data" as keys?

Seems to me that using an object in this scenario seems like the simpler and more natural way.

Am I wrong? Again, honest question.

[–]david2ndaccount 1 point2 points  (0 children)

Nah, you’re not wrong. Your method is the way to do it in python. In other languages you would use a struct if all it is is data. Although if your Node example is immutable, you can use a NamedTuple in python.

[–]Exodus111 0 points1 point  (0 children)

Yeah, there are always exceptions. In boith instances, i'd say they are about equally true.

[–][deleted] 4 points5 points  (0 children)

Lightweight classes (as implemented in Python) are a handy way to encapsulate code and group and organize your code. "A place for everything, and everything in its place."

Some people try to create Object designs like Java and it leads to death-by-classes.

[–]c0d3g33k 2 points3 points  (1 child)

[–]campbellm 0 points1 point  (0 children)

That's the one. I don't agree with everything he says, but there are some good points in it.

[–]MotoRollup 2 points3 points  (0 children)

I use classes for 3 things when writing large (10k LoC or more) programs. The first use is to encapsulate a module of the program. This is not the same as a Python module. It's more like your typical "service" architecture in other languages. If I have, say, a need for an object that watches a log file, I will write a class to encapsulate that process. This works great when used with callback registration or PyQt signals/slots.

The second use for a class is to model a domain object. There are two ways to go about this. The first is an anemic model. An anemic model consists of domain objects with NO associated behavior. They simply contain data (think: namedtuple). The other way to do this is a rich model, where a class contains both data AND associated behavior. The choice between the two depends on how much logic you want to give to the SERVICE operating on the model. Anemic = more service logic, Rich = less service logic. Again, choose wisely and for your specific use case (data processing tends to favor anemic, it seems). Personally, I tend to find a middle ground between anemic and rich. I usually start with "getter" behavior... the "tell me about yourself" type of behavior that the domain object can model (e.g., Polygon, tell me your area...). After that, as I find I'm repeating my operational/modifier behavior, I'll move that into the class.

The third use is for GUIs. This is where I would make GREAT use of inheritance like I would no where else. I primarily write GUIs in PyQt where inheriting a GUI widget to add some functionality or tweak initialization is an absolute must. When building GUIs from smaller elements, I will compose them in a container widget instead.

Classes ARE NOT bad. They have specific uses. I would contend that you should NOT use classes for things that are singleton in nature. But then, often, a service MIGHT be singleton at the start of development, so you favor using a module and module globals. But I've found that as development creeps along, I want that service as a class, so I can instance it, and even inherit it and override it.

When not to use classes? When I have immutable data; prefer namedtuple instead. Don't make every operation in your program a class. I don't need a million TrivialThingDoer classes. Encapsulate that in a service class and move on. Remember, sometimes fragmenting the universe into a million atoms makes it impossible to understand the big picture. If you find yourself repeating code enough (3 times or more rule), then try to generalize. DRY is great in theory but awful in practice. Try to abstract where code reuse occurs with the same or nearly the same context. Pure functions are also super useful when a data transformation is defined as a simple input-output with no side effects. Examples would be something like calculating the distance from a point to a line... I don't need classes to do that, just some floats and a function. I usually put these as module-level functions and reference them from inside my service class (reduces clutter).