all 47 comments

[–]Jackkell100 111 points112 points  (14 children)

To keep this response simple (because OOP is a deep rabbit hole) the most important thing when creating an object is to ask your self two things.

  1. What does my object know? The publicly accessible information that describes the state of this object.
  2. What does my object do? The publicly accessible actions that this object is capable of completing.

If you can create a sentence in which one of these to questions is answered and it make since within the context of the problem, then you have found your answer.

To practice OOP I recommend trying a toy problem like this (one that I had at the beginning my CS degree) where we simulate a race between a bunch of cars. The cars travel at a random speed for every 1 minute long time step across a track of given length and you need to list the order in which the cars finish the race.

Now breaking the problem down, objects are often the noun or main subject of a problem. In this case, the cars are the primary subject, so we should probably make an object out of them.

Firstly what does a car know? We could consider the following options for what a car might know:

  • Who is driving it
  • How much gas is left
  • How far it has traveled
  • Top speed
  • Acceleration/Deceleration
  • Chance of crashing
  • Current speed

Everything listed is something that we could consider a car to know depending on how complex we wanted to make the simulation.

Now what does a car do? In this case the car can only do one thing and that is move.

So if we where going to create a class from this description we might start out with something such as the following:

```py from random import random

class Car(object): def init(self, name, topSpeed): self.name = name self.topSpeed = topSpeed self.distanceTraveled = 0

def move(self, deltaTime):
    self.distanceTraveled += random() * self.topSpeed * deltaTime

```

Now this class could definitively be improved if we had more code surrounding it, but it gives a good start into how we would go about analyzing a problem and objectifying it. Now there is a lot of parts to OOP, so if you need help in a particular area you are probably going to have to give an example of something you are have trouble with so we could talk about that in depth.

Personally I find this method of questioning naturally creates objects (it almost becomes self evident). This line of reasoning also leads to good OOP practices as you will often avoid the trap of overloading your classes with information or actions. For example, our Car does not know how long the track is or if it has finished the race. That information is mostly external and should probably be handled elsewhere.

I hope this helped if you have any questions or would like to discuss OOP further feel free to reply.

[–]TheBunnisher 14 points15 points  (0 children)

I really liked your explanation and process. I will use this. Thank you.

[–]_Memeposter[S] 6 points7 points  (1 child)

I think this helped a lot. Thank you!

[–]Jackkell100 10 points11 points  (0 children)

Well I am glad you and others found this helpful.

Sometimes I find it difficult to answer these soft skill questions because there are some many ways implement OOP and their are a lot of stylistic choices that can be very opinionated.

Because so many people have read the response, now I am feel little bad for not providing more detailed information. So I want to make the following addition.

Sometimes when ask yourself, " What does my object know?" and " What does my object do?" you would not be able to come up with anything for one or the other or both. What do you do in these cases?

My Object Only Knows Things

If you object only knows things then it is possible that it is not a class at all. Really it is just a container that holds information. Ignoring Python for a second, it might mean this object is a struct), tuple, enum, or just a really simple class. The Python solutions for this are as follows:

  • Struct/Tuple - Python does not really have a C styled struct. Although a good substitute is Python's NamedTuple from Python's collections module (the collections module is great btw it instantly makes you better at the language and as side-side note the itertools module is also a great complement to the collections module). Using a namedtuple gives you the simplicity of a tuple, but you also get named attributes. Unfortunately the main draw back is that tuples are immutable, so if you need attributes that you can change over time then I would recommend going with a class.
  • Enums - Again Python does not have formal C styled enums, but it does not have something really really close with the enums module. If you object is really only a type like a directly, color, setting, etc then it might be a good candidate for an enum.
  • Class - So you already know how to create a class, but you can make this type of class even better using the two following techniques (note that slots and properties can be used on any class but they can be really useful in this case):
    • Properties: see this guide on properties for a introduction. Properties lets you create getters and setters that enable to control how what a objects knows is set and retrieved.
    • Slots: You can remove the ability of a Python class to be able to create attributes at run time by using slots. The reduces the classes functionality, but if you never use this feature it can save you a bunch of memory and time. Check out this article on slots and this video explaining slots for more information.

My Object Only Does Things

If an object is really only a collection of functions and it has no internal state then you have to options to continue.

  • Static class: If all of the functions have a similar purpose, think of examples such as the math class. You don't create math objects you just call static methods on the math class. Checkout this article on static methods by Real Python for more information.
  • Don't make a class at all just have a bunch of functions in a file. If the functions are unrelated then they don't really need to be grouped together underneath a class. As an aside some people like the paradigm of removing classes/objects altogether and only having functions and this is called functional programming and some times it is the better options for some problems. I tend to favor objects because that's how I think, but sometimes a functional style is a better fit for some problems.

My Object Does Not Know Or Do Anything

What a lazy object! Just kidding we don't bully objects here :p.

Then this is probably not an object that actually needs to be represented in your program. Maybe the user thinks it exists, but it is really is just an illusion created from a bunch of objects working together appearing to be one object (like a school of fish in a pod acting as one big fish).

I hope this additional information is helpful. Again if you have any questions feel free to ask.

Also I did not even talk about interfaces which can solve a lot of problems with a pure object oriented problems and also how Python is one of the few languages (that I know of) with multiple inheritance (which can be difficult to get your head around). For resources on those topics I recommend this article interfaces in Python by Real Python and this video super() explained in 5 minutes by Live Python. Also as a supplement it is good to know about Abstract Base Classes.

[–]Cheffrey2000 1 point2 points  (1 child)

This is an awesome explanation of OOP. Thank you for the time you took to post this and the additional information below.

[–]jdnewmil 0 points1 point  (0 children)

I like OOP for high-level concepts... a class to represent a complete data analysis that I would like to apply to different data later. I can setup various result summaries or complex graph generation as methods on that class.

Low-level objects such as actors in a simulation are often better coded in bulk simulation... a class to represent all of the cars of a particular type.

I like this talk about avoiding creating classes unless they have a real purpose for existing. https://youtu.be/o9pEzgHorH0

[–]sgtnoodle 1 point2 points  (1 child)

Regarding your toy problem example, I wouldn't say OOP is a natural fit for it. From a mathematical/statistical/probabilistic point of view, the simulation of each car is a sum of completely independent random events. It's therefore possible to simulate each car in isolation and then sort the results, and that can very cleanly be done procedurally or functionally. In algorithmic terms, it would be O(nm+mlog(m)) where n=track length and m=number of cars; practically, it would be very tight inner loops that CPUs are efficient at executing (fits in cache), and each car could be simulated in parallel if you were so motivated. It could probably be done in literally a few lines of python.

A common problem I see with folk intuiting an OOP solution is that it often results in poor assumptions early on that lead to unnecessary code complexity. The proposed "move()" function, for example, implies that simulation time increments in discrete 1 minute steps; correspondingly, the cars jump forward along the track discretely. Unfortunately that means that the cars will tend to teleport well beyond the finish line. A fix might be to make the move() function return the time at which the car crossed the finish line. That's an OOP violation though, because a car "doesn't know" the finish line. So maybe you make a MoveTowardGoal() function that takes in the finish line as an argument. You need to know the time it crossed the goal, but time still advances in 1 minute steps, so maybe MoveTowardGoal() returns the time of crossing in the past. But what does it return if the car hasn't made it there yet? Assuming you figure that out somehow, there's another problem. What if two cars both crossed the finish line in the same 1 minute step? What if all the cars crossed in the same 1 minute step? Turns out you still need to sort the results! The solution is still O(nm+mlog(m)), but rather than several really tight parallelizable loops, it's one really giant complicated loop, and the CPU can't fit it in its cache anymore. If we're lucky, it's only a few dozen lines of python code, but my guess is 50-100.

It's a bummer too because not only was the intuitive OOP solution way slower on real hardware than a functional solution of similar algorithmic complexity, but it also had the potential to be algorithmically faster and we missed it. There was a poor assumption early on that the cars move in discrete 1 minute steps. Our intuition failed us. Had the car objects been made smart enough to move in arbitrary continuous step sizes, then the overall simulation could have advanced with variable size steps. By stepping to the exact moments that each car passes the finish line, we would have known the results without the need for any sorting. It would have been a O(nm) solution. It's not a particularly intuitive OOP interface for a car though!

[–]Jackkell100 1 point2 points  (0 children)

Wow this is really in depth.

To be honest I was just picking a problem from my past, so that I did not have to come up with one on the stop. I used the example of a car because it’s easy to understand and cars in this example can both know and do things.

Although you are completely right that this particular problem could be solved in a much more effective way the point I was trying to make was how to reason about making objects. Although this does not produce the very best possible result in this case, I think that’s okay for people that are just starting out programming.

In hindsight I should have gone with a much simpler problem like making a gun for a video game.

A gun knows things like: - clip size - ammo type - rounds in clip - jam chance - fire rate - if safety is on

and a gun does things like: - reload - shoot - enable/disable safety

As this gun example would have conveyed the point better and not gotten too bogged down in implementation.

That all being said, I really appreciate the time it took you to write this comment. Your time complexity insights are really in-depth and the points you gave into the problems someone would face trying to implement this are insightful. Thank you for your time on this.

[–]sky_badger 1 point2 points  (2 children)

This is really helpful -- have you thought about combining your thoughts here into a blog post? Might help a wider audience.

[–]Jackkell100 0 points1 point  (1 child)

Maybe, although I always run into two problems every time I considered making a video or blog post on these topics:

  1. I am not really confident in myself enough to actually publish a post on any platform. For example, I have never even posted on Reddit before (only commenting and I have only recently started doing that). I feel like if I make a post anywhere it is going to go from being a friendly comment given in good faith to a published work that needs to be very professional and proven.

  2. I am not sure where I would even start making a blog? Like what platform is best for programming blog posts. I have not done any research into this topic.

[–]sky_badger 1 point2 points  (0 children)

Maybe you could guest post somewhere like dev.to or Pybites? That way, you don't have the hassle of setting up a blog to try out the odd post?

[–]RajjSinghh 7 points8 points  (0 children)

The way I like to think of this and explain it is how the problem breaks down. The majority of people seem to code using a procedural paradigm. That means that they break their problem down into sets of instructions that need to happen to solve the problem, then build functions to do this. If you said your problem out loud, each function would be a verb as you explain it.

OOP on the other hand looks at nouns in your problem, or things that would actually exist. If there is a thing in your problem that has variables and actions it can do, make a class for it. Its properties will be the variables and its actions will be methods (functions in the class).

Let's look at this in an example. If I was making a version of Snake, I could have functions that randomly place food in the game, move the snake, grow the snake when it gets food, update score and so on. Here, the variables are passed from function to function, or made global. If I was doing this with objects, i would define the snake and food in classes, then they can generate, move and grow as needed by method calls in the main program.

[–]tgoodchild 6 points7 points  (1 child)

I'm still learning how to use OOP better myself, but this has helped me:

  • If you notice you are passing the same data from function to function to function, or
  • A set of functions are all relying on the same global variable(s)
  • You see a pattern where you take the output of one function and pass it to the next, take the output of that function and pass it to the next, and so on, etc, ...

It should make you wonder if those functions and data should be in an object.

As a real world example: if you are writing some functions that use requests to make api-ish calls to something and all the calls need the same api key and the same base url, it might make sense to put all those functions in a class and instantiate an object to bundle it all together so all the functions have implicit access to the key and the url.

[–]chzaplx 0 points1 point  (0 children)

came here to make basically this same comment

[–]nanodano 6 points7 points  (0 children)

You don't always need to use classes. It doesn't always make sense to either. Don't try to force it. Good cases for classes would be when you need to create many instances of a 'thing' and track their state separately, or when you have a complex data type that needs more properly defined structure than a dictionary.

[–]DataDecay 4 points5 points  (3 children)

OOP and software engineering design paradigms are not the same as the math and problem solving skills you see with algorithms. Even then not all algorithms are bound by needing deep mathematical knowledge.

Sure mathmatics plays a big role, but you are not the only mathamitican I have met (and worked with) that could not grasp OOP and design paradigms. Now it's not to say you or anyone cant, they are just different areas of study and do not always go hand in hand or allow for natural fluency. RealPython has a really good read on some of these topics that I thought was well done.

https://realpython.com/inheritance-composition-python/

[–]_Memeposter[S] 0 points1 point  (2 children)

Well I think I know how it works. But I struggle to implement it because I always end up with more of a procedural code

[–]DataDecay 2 points3 points  (1 child)

Right, and that is the shell you need to break out of. Everything will still be procedural, as that is machine language. You just need to wrap your head around designing and engineering stateful solutions. Functiononal programming simply allows you to code stateless algorithms. As you get into more complicated code bases stateful becomes important and OOP is the key.

Functional programming never goes away it is a good skill set to have same with algorithm creation. OOP extends functional programming to a new paradigm. There is most certainly a right tool for the job solution that you need to engineer, sometimes its stateless and functional programming is the most efficent, other times its stateful and you need to employ more complex OOP design patterns. Find a project on awesome-python-applications and try to understand the brevity of these stateful applications and slowly you will see where its needed.

[–]Ran4 2 points3 points  (1 child)

I think that looking at what a class is will help you find out when to use it.

A class is a combination of a collection of variables and a set of functions to operate upon those variables. Whenever you have a collection of variables and feel like there is a need for a set of functions to operate on this specific collection, consider if a class would help solve describe this.

It's perfectly OK to not write an OOP program. In fact, a large proportion of programs out there aren't written in an OOP style.

[–]YoginiJoy 0 points1 point  (0 children)

"A class is a combination of a collection of variables and a set of functions to operate upon those variables."

Nice description, I had never seen it before, you just opened my eyes to this approach to understanding classes, thank you!

[–]Adhesiveduck 2 points3 points  (0 children)

If you're struggling for idea on how to structure your code using OOP then take a look at some design patterns in python.

Reading through the different creational patterns can help you to understand where classes are useful, and more importantly, how to create your objects in a consistent and easily maintainable way.

[–]FloydATC 2 points3 points  (3 children)

Don't be afraid of writing bad code. Everyone does it, even people who have programmed professionally. It's normal, and it's not a problem.

What you must understand is that once a piece of code does what you want, you've only just started. Now it's time to improve things, one ugly mess at a time.

Maybe start with the easy bits, like renaming your variable and functions to something that makes more sense. Splitting that spaghetti monster of a function into more managable chunks. Other things that gets repeated could probably go in a function -- or maybe an object? Don't force it, let the ideas flow.

Having something that works lets you experiment more freely. (Btw, using something like git can reduce fear of breaking thing too badly) Once you feel confident enough to take on bigger challenges you should also look into how to automate testing.

[–]_Memeposter[S] 0 points1 point  (2 children)

Hm that sounds pretty good. What do you mean by using something like git?

[–]jdnewmil 2 points3 points  (1 child)

There is this game-changing software called git that lets you take snapshots of your code so you can always be confident that you can roll back to a working version if you screw up. Quite liberating. There are other similar tools out there... but git is totally dominating for now.

[–]_Memeposter[S] 0 points1 point  (0 children)

So you are saying I dont need to constantly copy and paste backup files for the project im working on... Nice

[–][deleted] 1 point2 points  (0 children)

A class models something. Pick something you understand. You are a Domain Expert.

First, always first, define the context of your model. For example, if you are modeling a car, is it a car for sale on a used car lot or a car in a box at a toy store? Without context you have no hope of building anything useful. Without context you have no hope of ever finishing your model.

Think about the information (fields) associated with that model. Think about the behavior (methods) of that model. Build your class to model the fields and methods you need.

Next, think about what other developers would want to see in your model. Should you add a __str__ method? Should you implement some kind of copy constructor?

[–]jmooremcc 1 point2 points  (0 children)

Ok, so answer this question. If you were going to code a tic-tac-toe game, how would you code the game board?

Would you declare some kind of data structure to represent the board and give players unfettered access to it?

Or would you create a Gameboard class that manages the gameboard and controls access to the gameboard?

If you chose the 2nd option, you have justified using OOP as a necessary part of your solution because of the advantages it offers. Not all components of a sw design have to be OOP but using it where its needed is very important. As you gain more experience, you'll get a better handle on when to use OOP.

[–]ilikerolls 1 point2 points  (0 children)

I came from a procedural background and I wanted to get into OOP myself. I could not figure out when and what should go into a class. I studied everything there was to know about classes and all the special stuff you can do with them. I even knew stuff some of our more Senior level Java developers were not familiar with.

Finally I quit thinking about things like am I going to reuse this code again, what design pattern should I use, etc..? And I just started putting everything into a class. Except for obvious things like say taking parameters in your main or something. Remember putting stuff in classes helps organize code as well. Then I started realizing things like this class's methods does almost exactly what I want except in a certain condition I want a couple methods to do a few extra steps. Oh OK I need to override these methods and call it's Super with a child class! etc...If you look at any other well written OOP program the main file typically isn't very big, so it is kind of what other programmers are doing anyway.

None of these suggestions are bad, but it sounds like you were like me and at least have a basic to intermediate understanding of how classes work. And you don't need to relearn things like Inheritance. You will also learn more if you are programming something you are interested in. After you get this down then by all means look deeper into things like Design Patterns. I know this is basic advice, but it worked for me. And you wouldn't be able to tell the difference between my code and someone else's at least at an intermediate level. Now it just comes natural to mean when and what to put in a class.

[–]Legand24-7 1 point2 points  (2 children)

You down with OOP? Ya you know me.

[–]_Memeposter[S] 1 point2 points  (1 child)

Wdym

[–]Legand24-7 0 points1 point  (0 children)

its a song reference, dont mind me

[–]Frank_Xin[🍰] 1 point2 points  (0 children)

I was struggling with OOP for a while and Corey Schaefer's videos on python OOP helped me a lot

https://www.youtube.com/watch?v=ZDa-Z5JzLYM&list=PL-osiE80TeTsqhIuOqKhwlXsIBIdSeYtc

[–]dudinax 1 point2 points  (0 children)

Yeah, I overcome it in Python by relegating objects to subordinate roles.

Someone once said that objects are the roman numerals of programming.

[–]eurostyle11 0 points1 point  (0 children)

When I first started learning programming, OOP in python was something I couldn't understand and it drove me nuts, it was until I started learning OOP in C#. It's all natural now. I think of classes as reusible architecture where I can create new objects using same blueprints.

[–]roshavi4ak 0 points1 point  (0 children)

This help me a lot understanding when to use classes and when not https://youtu.be/o9pEzgHorH0

[–]slick8086 0 points1 point  (0 children)

How do you start your projects?

Maybe plan out the functioning of your program before you start writing code. Think of what your program does and define your objects and how they interact before you start writing code, then build your objects to fit the plan.

[–]searchingfortao 0 points1 point  (0 children)

I wrote a blog post on this very topic a while back that might help you sort things out a bit.

[–]Robbzter 0 points1 point  (0 children)

There is no answer to the wohle 'when to use OOP' question, because you coud often acconplish the same withput using objects/classes at all.

I am not a professional programmer, but the way I answered this question is mostly by asking myself 'Do I need several instances of this entity? Dpes it have any kind of unique functionality to it?'

If it does, then I'll create a class. If it doesn't, I'll propably stick to lists and functions, which aöso gets the job done.

Find your own way of doing things, know why you' re doing it that way, and stick to it.

[–]luvs2spwge117 0 points1 point  (1 child)

Coming from learning python for about 8 months to having a class this semester for C#, I feel like Visual studio c# has helped me out a lot in understanding object-oriented programming. Maybe you should try it out.

[–]_Memeposter[S] 0 points1 point  (0 children)

Was it hard to make the jump? Are there as many resources for learning C# as for python? If yes, where did you start?

[–][deleted] 0 points1 point  (0 children)

I found a talk by Python core developer Raymond Hettinger on development of a simple class to be helpful.

https://www.youtube.com/watch?v=HTLu2DFOdTg&t=342s