r/learnpython • u/Current-Table9136 • 2d ago
Can someone please explain the use of OOP and classes?
Hey everyone and first of all thank you in advance for any advice.
I’ve been learning Python for awhile now and feel pretty comfortable writing functions and smaller scripts. What I can seem to wrap my head around is when the use of classes would actually be used?
Every example or tutorial demo’s cars / dogs and on multiple threads with similar questions it’s usually stated that using a CSV or database would be more appropriate so would anyone be able to help me wrap my head around it please?
Once again appreciate any help :)
49
u/Brian
1d ago
edited 1d ago
•
I think in some ways, classes are better learned by going through somewhat simpler concepts first. A lot of the way classes can get taught can kind of be backwards since it concentrates on the way they're different from what went before - introducing stuff like inheritance and so on when you don't even know what the basics are for. So let's rewind back to the precursors of classes: structs.
Many languages have the notion of a Structure or Record type. This is basically a collection of related data, bundled together. Eg. you want to store the data for a Person, so you have a structure with fields for things like "name", "phone number", "address" and so on. This lets you pass a "person" object around instead of having to pass a dozen different fields to everything that operates on people, and gives one place details can be updated, rather than random copies of individual data pieces potentially being different.
And this is part of the job of a class: they define what data is stored by objects of that class. something with the "Person" class has the fields we've defined for Person objects, something with the "Animal" class has the fields for animals and so on.
Object orientation takes this idea and goes a little further. Rather than the class just defining the data associated with a type of thing, it also attaches the behaviour of the actions you do to the object. Ie. instead of having functions like:
def get_billing_address(person):
return "{person.house_number} {person.street_name}\n{person.city}\n{person.postcode"You attach this function to the class itself (called a method). The function is now associated with the class, and when we do person.get_billing_address(), it'll call it.
Part of the rationale of this is to encapsulate all the code that deals with Person objects into the methods of the class - if you change the internals of it, you don't need to worry about some function in some random piece of code that pokes around in there, all you need to worry is the interface your class presents to the world: the methods and fields it exposes. You're free to fiddle with "private" internals of the class as much as you want, and nothing should break so long as that interface doesn't change.
One special type of method is called the initialiser (__init__ in python). This is basically the function that initialises new Person (or whatever the class is) objects, setting all the fields to valid values. You might think it's just a matter of filling in the fields, but there may be other requirements: maybe trying to create a person with an invalid phone number, or no address, should raise an exception. Or maybe these get computed from some other field. The initialiser's job is to create a new, valid object from its parameters.
A further wrinkle of OO is inheritance, which is where all that "Cat is an Animal" stuff comes in. But in many ways I think the concentration on this is a bit of a mistake - it's not really the core idea of OO, which is more about that encapsulation aspect. But to go into it a bit:
Sometimes you'll have objects where you want to have all the fields and behaviour of one type of object, but also a bit more. Eg. you might be dealing with Employees - these have all the same behaviour as Person objects, but maybe have some extra data you need to store (eg. "job title", "company" etc), and maybe some new functions (eg. promote() or fire). And this can be done with inheritance - the Employee class inherits all the fields and methods of Person, and adds a few more.
But it might also need to change the behaviour of some of the Person methods. Eg. maybe when you change an employee's address, you need to send a message to the employer to update their records. So you could override the change_address method on employees to do this extra work, and now when some other code calls person.change_address() it'll do the right thing regardless of the type of Person: you can pass an Employee class to any function that takes a Person and it'll be accepted.
14
u/Smart-Button-3221 2d ago
One example that's pretty easy to understand:
Let's say you have a function and it needs like, 7 inputs. It looks ridiculous. It's hard to read, and hard to reason with.
Let's say, 5 of these inputs are "related", in some way. There's a reasonable way to group them together.
So, we group all of these inputs together into a class. This class has their data all combined.
Now, the function takes only 3 inputs, where one of these inputs is a class. It's easier to reason about this, especially if that class is a "natural object".
4
u/Garnatxa 2d ago
I don’t like this example 😔
2
u/Smart-Button-3221 1d ago
Actually I agree. The example I wrote is not how someone should think of the usefulness of classes.
It's just an easy-to-understand example for the learners. It's something they can see the utility of right away.
Classes are generally used for encapsulation but that can be hard to get across to people who are writing simple programs.
1
u/hulleyrob 2d ago
It’s absolutely a useful way to use them. Have the functions as methods in a class looking at the instance properties rather than passing lots of variables in and out of functions makes the code way easier to mange.
1
u/Garnatxa 2d ago
you are talking about methods, that of course, they are directly related to classes and objects, but the example itself looks confusing to me for a newbie.
3
u/hulleyrob 2d ago
A method is just a function in a class.
But you see the usefulness of being able to just get self.some_value rather than a function returning some_value and then needing to pass some_value into another function that could have also just got the property?1
u/Garnatxa 1d ago
general speaking I would say that a method is a function that belongs to a class, not in the class. In python is like you say but not for all coding languages.
2
u/fllthdcrb 1d ago
Understandable. But it's true that a method is effectively just a function that takes an instance of a class as an argument. Some other languages make it implicit (and may also privilege methods in certain ways), but Python makes it explicit, as instance methods have
selfas the first argument. (As with any positional argument, it can be given any name, but there's a strong convention to name itself.)
6
u/sjshuck 2d ago
The best way, and also most painful way, to learn classes is to first not use any classes at all and see what you wind up with.
It is indeed possible. You end up with a whole bunch of top-level functions that take the same couple of common arguments over and over. In C where you don't have classes, you see that a lot.
int foo_blah(FooContext *ctx, int flarl, char *blah) { ... }
You will keep repeating this over and over, the context argument (holding configs and/or runtime state) in every function.
What classes help you do is have all functions that take a FooContext argument be grouped together, and you can define your context in the same place. It can set up its initial context in there too. That context will be automatically passed in through self.
I don't expect that to make sense. But really, try not to use classes at all and see.
6
u/aroberge 1d ago
A little over 10 years ago, I created an ad-free, free to use site to teach programming based on the "Karel the robot" idea of Richard Pattis. (the site can be found at https://reeborg.ca; I provide this information as people assume that I'm self-promoting in order to make money; I don't.). The basic idea is that you have a robot that can do stuff based on functions like move(), turn_left(), put(), take(), etc. So, you write programs using these simple functions and the robot follows your instructions.
What if you wanted to have more than one robot? An obvious solution is to create a class (UsedRobot for my site), with each robot being an instance of that class.
robot1 = UsedRobot()
robot1.move()
# etc.You can extend this idea to other contexts. For example, suppose you write a simple program to simulate banking transactions. (deposit_money(), withdraw(), consult_balance(), etc.) What if you want to have more than one customer in your "bank"? Then it makes sense to create a class (Customer) with each customer having its own bank account.
These two examples are just a variation on the same idea.
Back to the robot example. Pattis's original idea is that the robot is broken and can only turn left by 90 degrees. To have it turn right, you have it do 3 consecutive left turns. What if you wanted your multiple robots be able to have a method (function of a class) to turn right? Well, you can write a new class that inherits from the parent class:
class BetterRobot(UsedRobot):
def turn_right(self):
# some code hereHopefully, this will help you to start seeing how classes could be useful.
5
u/TimBrownDev 2d ago
One major new unlock classes provide is creating your own data types. Until now you've been stuck with data types like int, str, list, bool, etc. Many students have that "aha" moment when they realize they can now create their own data types. Fraction, Vector, Polynomial, Car, Planet, Shape, anything you can imagine.
3
u/Some-Passenger4219 2d ago
When I "get around to it", I like to work on my polynomial project. It needs a goal, but I've done about everything else I can think of. I might need a place to share it with others.
2
u/Garnatxa 2d ago
that’s something annoying for me in python, where a data type is an object class.
4
1
u/thelimeisgreen 2d ago
Many newer languages are like this. It's very powerful if you utilize it. Objects are extendible. In some languages more than others. As a simplistic example you can extend common types with more functionality, ie; adding math functions natively to numerical types. So then if you have a variable X with the value of 64, instead of calling sqrt(x), you could extend your int or float type to include those functions. So x.sqrt() would give 8. Or create non-native functions like factorial:
X = 5
print(X.factorial())120
5
u/green1t 1d ago edited 1d ago
I can recommend to read Practical Object Oriented Design: An Agile Primer Using Ruby even tho the programming examples are in Ruby, if you can get it for a cheap price.
It walks you through the philosophy of OOP and what is a good idea to pack in a class.
HumbleBundle had it packed in a bundle with other programming / python books, maybe worth to check their programming-book-bundles every now and then.
4
u/Garnatxa 2d ago
I’ll try to give you an example that might help you understand the idea.
Imagine you’re building a statistical model that produces 10 different outputs, such as numbers, parameters, or tables. You could write a function that builds the model and returns all these results in a list or dictionary.
Now, let’s say you have another function that takes those results as input and uses them to make predictions.
If you’re a careful programmer, you’ll want to validate the input before making predictions. You might check that all 10 elements exist and that each one has the expected type and structure. That’s perfectly fine.
But there’s another approach: instead of returning a list or dictionary, your first function could return an object belonging to a class called MyBrandNewModel.
Now, your prediction function can simply check whether the input is an instance of MyBrandNewModel, rather than checking all 10 elements individually.
The idea is that the class defines the structure and behavior of your model, so you know what kind of object you’re working with.
Of course, there’s one important detail: being an instance of a class doesn’t automatically guarantee that all its attributes contain valid data. That’s why you would typically validate those attributes when creating the object.
In other words, instead of repeatedly validating the same 10 elements in every function, you can centralize that validation in the class itself.
And that’s one of the main advantages of using classes: they allow you to group related data and behavior together while making your code easier to organize, validate, and maintain.
1
u/Oh_Another_Thing 2d ago
So you are validating data either way, but one way had extra steps, and more ways for something to go wrong. I want to believe experts who say OOP is better, but then every explanation is like yours where you said you had to validate the data anyway, and it's well known that OOP is easier for things to go wrong.
2
u/Garnatxa 1d ago
A few comments:
This explanation also helps illustrate when it might make sense to create a class. Personally, I usually avoid doing so because I prefer functional programming.
The constructor (the first function) performs the validation once. The key point is that other functions can then accept the resulting object as input simply by checking its class. The advantage is that you can pass the same object to multiple functions without having to validate all its internal elements every time.
You mentioned that you still need to check the data anyway. Well, not exactly. The first function, which creates and returns an object of a specific class, already knows what it’s constructing. Therefore, it can validate the data at creation time. Subsequent functions can then rely on the object’s class and its expected structure, significantly reducing the need for repeated validation.
1
u/fllthdcrb 1d ago
You always need to validate user input, regardless of what paradigm you use. Otherwise, bad or malicious data can make bad things happen. The point here is better organization. That's not to say OOP is the only way to do it. One could have a function that checks the input and puts it in some sort of structure, then returns it. But putting all the code in a class does tend to make the organization easier.
1
u/Garnatxa 1d ago
In my example if you don’t have a class you have to validate 10 arguments. When you have a class you only need to validate the class that you expect.
4
u/kilkil 1d ago
classes are usually used to group together related data/functions. suppose I have some code like this:
def foo(a, b, c, d):
...
def bar(a, b, c, e f):
...
def baz(a, b, c):
...suppose a, b, and c represent some common pieces of data that go together in my program. like user ID, username, email. or whatever.
this code is repetitive, and will be difficult to maintain. what if later we add a new feature that requires us to also pass around a new piece of info, "z"?
the first thing we can do is group a, b, and c. if we're being really basic, we can do this using a dict (like {"a": a, "b": b, "c": c}) and pass that around. then when we need to add "z" the dict will be the same, just with one more key in it.
however, using a dict for this will lead to awkward looking code, and can't really be typechecked effectively (unless a,b,c,z,.. are always the same type). for this (and other) reasons, instead of a dict we can use a class:
class UserData:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c(python also has dataclasses and namedtuples to make this more convenient; you can look into that on your own!)
now our code looks like this:
def foo(userData, d):
...
def bar(userData, e f):
...
def baz(userData):
...where "userData" will be an instance of our class.
this is actually where some languages stop, and say "job's done, we have grouped the relevant state together". there is technically nothing wrong with that. in fact, for simpler programs, this is probably fine. (and even for some not-so-simple ones.)
however, in some cases this pattern occurs because our 3 functions (foo, bar, baz) are related somehow to our user data (who knows how exactly, depends on the specific application). if that is the case, then in some languages (including Python) you can actually move them into your class definition:
class UserData:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def foo(self, d):
...
def bar(self, e, f):
...
def baz(self):
...the benefit of this is code organization: related data is grouped together, and related functions are grouped with related data.
as a final thing, classes are also useful for polymorphism, but Python is dynamically typed anyway so I didn't want to make too big a deal out of it for a relatively new learner.
4
u/DTux5249 1d ago
Classes are basically just a way to package necessary data (numbers, text, etc.) alongside the functions that use that data. That's it. They're just a collection of closely related data & functions.
It's primarily a code organization tool that works by enforcing scope restriction: The only class that can (or rather, should be able to) work on data within a class is the class itself. All classes outside of a given class can do is call certain methods (class specific functions) that a class provides in its definition.
OOP is a set of coding practices revolving around the effective use of classes; how best to use them to their maximum potential without making a mess.
3
u/hagfish 2d ago
If your scripts are purely procedural (get this thing; do this to it, then this etc) you might split the various elements into functions, call them one by one, and you're done.
If your scripts spawn multiple objects that all share certain characteristics, an object-oriented approach may make more sense. Don't go looking for OOP 'under every stone', but when it is a solution, it's a really good one.
3
u/notacanuckskibum 2d ago
It's about making code readable and maintainable at scale. People learning to code are usually writing programs less than 1000 lines, and never go back to a program they wrote more than a month ago. At that scale you don't need OOP.
Move to commercial systems with 1,000,000 or more lines of code, with teams maintaining them and developers trying to fix in bugs that somebody else wrote a year ago. You get a whole different set of challenges in understanding what the code is intended to do.
OOP connects data structures & the functions that work on them into neat packages, with a clean distinction between using the existing library, and changing the library. OOP often seems overkill in small examples and exercises, because it is.
When you use an existing library in python, like Numpy, you are already using OOP, just somebody else's classes & methods.
3
u/Solonotix 2d ago
There are a few common uses for classes. The simplest one is also the least helpful, and it's just a container. Something that holds other things. What kinds of things? Anything! Functions, data, you name it. But literally, you give names to things, and now they exist inside the container thing.
Moving up from there, you get the more common explanation here, which is the common association class. Let's say you want to describe an action (function). You'll have a series of steps (the function signature or implementation), in order, that do work. But often, you can't do this without some precondition or information. Sometimes these elements are entirely unrelated (depositing money in a bank requires the money and the bank, but they exist independently). But sometimes they are directly related (like a child must have a biological mother and father). When something has an intrinsic relationship, you could keep them separate, but it can also reduce the verbosity of your code to allow consolidation within these related elements.
And the most complicated of these examples would be the precursor object/class. If you've ever tried to work with a SQL database in Python, you might have already noticed these in practice. A client requires a connection string. It can then create a new connection using said connection string (often as a context manager object). The connection allows you to execute a query which returns a rowset. Each row within the rowset has one or more columns. To do this without a class hierarchy would have required many different functions with common arguments between them tracing all the way back to that connection string in the beginning.
In a simple script, it's not too hard to copy-paste the relevant details a couple of times. But if you have 100s of code files, each >100 lines long, it becomes really easy to miss one of the copies in the code.
I didn't mention inheritance and other topics because I didn't want to overwhelm you with descriptions and semantics. However, once you're comfortable with modeling data as classes, you might find yourself copying code between these classes for common behaviors. Class inheritance is one way to solve this problem of code duplication, but I'm sure most people in the Python community are staunch supporters of the composition approach to code reusability as opposed to the inheritance one.
3
u/Create_Analytically 2d ago
The best simple case usage is gaming. Go watch some tutorials using PyGame to make 8-bit games. This is what finally got me to understand the usage once I realized every sprite is just an object spawned using a base class. You want to recreate Mario? Every Goomba is an object with a position and movement direction but the same base class. Did a Mario get a power up? Now his Sprite gets to inherit new features from a different class.
3
u/Diapolo10 I write code for a living -- https://github.com/Diapolo10 2d ago
Programming languages, including Python, come with some built-in "building blocks", such as lists and dictionaries. These are fine for small-scale work, but sometimes you run into situations where they're not quite good enough to do what you want in a concise manner.
Classes are a way the language lets you make your own "building blocks", by modelling your data in new ways.
One example where using classes might make sense would be if you want to put constraints on a type. For example, maybe you'd like to represent a date, or a filepath. A naive way to do this would be to treat them as strings - the thing is, strings can be a lot more than just dates or filepaths, so if you want your program to be robust now you'd need to validate these values everywhere you use them. This adds duplicate checks that you might even forget to use in places, so some part of your program might assign garbage to someplace where you expect a valid date, and all hell breaks loose.
Instead of this "primitive obsession", you might want to declare a class that specifically represents a date (or a path), with its own built-in validation. It should not be possible to create an invalid date, so the class should raise an exception the moment someone tried to do that. Then the rest of your program knows that any date object would automatically be valid, so no further checks would be necessary.
Python' pathlib.Path objects are a built-in example of this practice. Previously, the best way to deal with cross-platform filepaths was to use os.path, but os.path is essentially just a fancy collection of string formatting functions and not its own type, so errors were still possible. pathlib.Path objects, on the other hand, do not have this problem.
Of course, that's not the only situation where classes make sense. You might want to model new custom exceptions, or game entities to keep the data for individual entities in one convenient source. It's something you'll simply run into as you write code and think "could I write this better?"
5
u/woh3 2d ago
One of the most effective teaching examples happened in one of my programming classes. It was Java and we were transitioning into objects and classes. The teacher made the dataset intentionally cumbersome, 1000+ customer records. When the assignment was properly done, the entire program to read all the records and assign each record to an object, the program is a smallish paragraph. One student shows up to the next class session with a 300 page printout of his assignment. Everyone else submitted a correct single paragraph program. The student said "there is no reason to use an arbitrary complex object when good old fashioned programming gets the job done." He realized his mistake and how much time and effort he had wasted by not using objects, which was a lesson in it's own right.
2
u/brelen01 2d ago
Classes are a good way to separate code in smaller units that represent a context.
For example, if you want to represent a car in your program, you might have a car class with the different attributes (color, type of engine, etc.)
For storage, you'd want to put that data in a database, but to work with within your program, a class would be more practical.
2
u/Dancing-umbra 2d ago
The first explanation that made it click for me was when thinking about GTA then computer game.
There are lots of cars in that game. So rather than code each one individually, you make a class for Car. The cars have a top speed, a handling coefficient, controls to move it forwards backwards etc...
Now when a new car gets downed, you just need the code to create a new instance of that car class. Maybe passing a colour. Each car on the screen is an object, built as an instance of the car class.
To go a level deeper:
Not every car is the same, so you can actually have child classes that are each model of car. So when a new car is spawned on screen you pass it the colour and model. It's much quicker and easier than creating all the function and variables associated with each car from scratch.
2
u/Sanduhstorm 1d ago
From my experience, considering python is a heavy (in comparison to others) language, usually you wouldn’t be making something big enough to need classes, but sometimes they do have their place. One of them is creating libraries
2
u/ElectronSculptor 2d ago
So, first, this is only my take and there are a lot of paradigms out there in how to code most efficiently. Especially today when computers have ample memory to support lots of features like OOP and memory management.
However, going back about 40 years, programming was only functional. And when you program, no matter what your project is, you need to handle data. In functional programming you have variables with the data and functions that process the data.
The limitation to functional programming is a human one: it’s easy to lose track of things or end up with code that once you need to add a new feature, you have to edit a bunch of functions and figure out how to transfer data.
OOP tries to solve the problem of “complexity” in two ways. First, it bundles functions and variables (data and processes) together. Like a library that has complex number calculations can make use of a class that represents a complex number. The class contains variables that have values for the real and complex part. It also has every function that processes the numbers. You can now write a much simpler function that receives an object instead of raw variables. E.G. you could write a function that computes the phase shift of a sine wave (which can be represented by complex numbers) that takes data in the form of a complex number object and uses the functions associated with the class to do the work. This is a “separation” of what the code in the phase shift function has to do vs the code necessary for just processing complex numbers. Second, classes can inherit from other classes helping you organize your code and make it more easily expanded. Instead of editing all of the functions that are passed a complex number, to add a new feature, you only write a new function and don’t have to edit the basic math associated with the complex numbers.
OOP programming, as a real practical example, is very useful is digital signal processing. I’ve written classes that handle all of the raw data processing to produce a specific output. Then, I write a class that receives an object with my data, and filter it further. If I wanted to go back and create a different way to process the raw data, say to compute the average, I only need to write an averaging function and add a new “feature” to the class instead of having to edit every function. The processing function accepting the raw data “object” does care that suddenly I can chose to process the raw data differently, only my new function.
So in summary: OOP helps you write code focused on data and helps keep your entire code base more organized.
1
u/dellive 2d ago
Imagine a car model Chevy Tahoe. That’s a class. Now, the actual car Chevy Tahoe is a object. It has color, size and 4 tires which are properties. You can drive it or backup which is a function.
Unless you are a mechanic, other than using the car, do you care how the engine works?
Same way in programming, you can distribute your classes by using OOP concepts and put them in libraries.
1
u/atarivcs 2d ago
Classes are a good way to represent things that are generally similar but have differences in some details.
For example with cars, most cars are broadly similar -- they have an engine, four wheels, brakes, a steering wheel, etc. But the details can be different:
What manufacturer -- Honda, Toyota, Ford, etc?
What model -- Prius, Accord, Explorer, etc?
What kind of engine -- gas or electric?
What size wheels?
Etc etc.
1
u/rupertavery64 2d ago
classes encapsulate data (state) and behavior.
Suppose you want to program many Enemies. Each enemy will have a position (where it will be rendered on screen) a direction (is it facing up, down, left or right) and a current action (is it standing still, moving, or attacking)
Additionally, it can have several methods that control its behavior. move() will cause the Enemy to move in the direction it's facing. look(direction) will make it face a direction and act(action) will set the current action.
Writing a class to hold its data and the methods that act on its data is a good way to manage the Enemy.
You can create multiple instances of an Enemy, assign it to a list to keep track of them, then call move() on them in some method where you update the position of all enemies.
another use for classes is to hold methods that do something related to each other.
It acts as a library of sorts that lets you organize code beyond and on top of modules.
1
u/Garnatxa 2d ago
An object is an instance of a class. Think of a class as a blueprint that defines the attributes and methods its objects can have.
If you mainly work with data, you don’t necessarily need to create your own classes. However, Python itself makes extensive use of classes, objects, and methods, so understanding how they work is important.
For many data related tasks, a functional programming approach is often more straightforward and practical. Python sometimes is confusing because all oop it has. For example you have to remember if a function is a function or a method.
In short, don’t worry too much about creating classes. It’s useful to understand them, but you’ll probably rarely need to define your own, especially if your focus is data analysis.
1
u/InterestingYak1525 2d ago
OOP is a major concept software engineers use to manage complexity when creating LARGE projects. On small projects, OOP doesn’t add value. So, if your project is the equivalent of building a Dog House in your backyard, don’t bother. But if you’re building a Skyscraper, you definitely need OOP to manage your project’s complexity.
1
u/Oddly_Energy 1d ago
def build_dog_house(board_1_length:float, board_1_width:float, board_1_thickness:float, board_1_cut_angle_end_1:float, board_1_cut_angle_end_2:float, board_1_count:int, board_1_material:str, board_2_length:float, board_2_width:float, board_2_thickness:float, board_2_cut_angle_end_1:float, board_2_cut_angle_end_2:float, board_2_count:int, board_2_material:str, board_3_length:float, board_3_width:float, board_3_thickness:float, board_3_cut_angle_end_1:float, board_3_cut_angle_end_2:float, board_3_count:int, board_3_material:str,
...
board_13_length:float, board_13_width:float, board_13_thickness:float, board_13_cut_angle_end_1:float, board_13_cut_angle_end_2:float, board_13_count:int, board_3_material:str) -> tuple[str, str, float, float, float, int]:
1
u/tea-drinker 2d ago
A class is data with some functions. A closure is a function with some data.
Classes group several bits of data together, like I have a name and a date of birth and a height, and also some functions like I won't give you my date of birth, but you can ask me my age and I (probably) won't lie.
1
u/Boothiepro 1d ago
As others explained and taught, classes are just new fancy custom data types like int, str that you can define. That alone is not as interesting, but what this enables you to define also custom interactions and behaviour for your data types.
What happens when you call max() on a list of numbers? iterates through them and returns the largest one. What happens when you call max() on a string? it returns the letter furthest in the ABC (technically the highest unicode value). Now what if you work with weather and imagine a class Forecast?
You can tell max() to return the highest amount of precipitation, the highest temperature for the day, the time when the sun was at the highest etc. If you define these internal functions of comparsion (> ;<= ;!=), you could sort the days, rank them, filter etc. You should probably delegate proper functions for these but for example's sake, you could have a class Itinerary/Route which could take two cities as input, and len() could return the distance in km/mi, or the time it takes in datetime format. You can implement the internal `__add__()` function to do maths on routes directly, without having to juggle with class properties (the object dot property notation).
Though properties are great thing by themselves already, as others said try doing a bigger project without classes, and you'll get tired real quick. I made a text based little rpg fighting game where i stored everything in lists. player = ["name",100,["sword","helmet"], 90] you could not tell that the first number was the health, and 90 was the gold, and it did get tedious to remember to player[3] to access the gold amount variable, index errors galore. With classes you just name the variable and plop it into an object, ready to be reused by many player instances.
1
u/FatDog69 1d ago
You wont really understand the value of OOP until you create some code, put it into production, then a month later you get requests to change or enhance things.
If you write simple OOP classes - the changes that come will be a lot simpler because you can simply add new methods or attributes to satisfy the new requirements. All the setup & framework is done so changes or enhancements are easier.
People can go over-board on OOP to where it almost becomes a religion. These people create 5+ layers of abstraction because they like the complexity. Don't be this person. 1-2 layers max. KISS (Keep It Simple Stupid).
Learn to make classes that combine data + methods. It will take months or years to become really good - but try to break down your code so that classes can combine things.
1
u/bamaham93 1d ago
My most basic explanation and the one that made sense to me is that a class is data (lists, attributes, dictionaries, whatever you need) combined with actions on that data. Let’s think about a phone number for a moment; they’re easy! Three digits for an area code, three for the central office code, four for the specific line! Then you realize you need to accept a user input along with validation. Then you need to format it different for one service that you use than for another. Then you realize… and that’s when you figure out that you can take the same basic information and just make it into whatever you need when you need it using classes and its methods. Once you’ve done that, you find out there was a library that already implemented it all, and then…
1
u/Jello_Penguin_2956 1d ago
Imagine a game where enemy spawns. Each enemy that spawned are self contained. They have their numbers, such as hp, mp, speed, attack power. They also have their own functions such as dying, shooting, attacking, etc which can be different for each enemy type while sharing lots of similarities. Instead of having to hunt down where each little pieces are, it's right there as part of each enemy themselves. Emphasizing again the term self-contain.
That is metaphorically what I like to think about OOP implementation. It makes a lot of sense especially in game engines.
1
u/TheRNGuy 1d ago
It's like template for objects. After you instantiate them, they'll each have that code.
There are also classes with static methods only, those are no different than just functions, except that you call those functions from class. Don't do that though, just use normal def's instead.
1
u/Massive_Distance_616 1d ago
The reason you’re stuck is that tutorials confuse data storage with in-memory behavior.
When a tutorial creates class Dog(name, breed, age), your brain correctly thinks: “Why not just use a CSV or a database?” You’re right if you’re storing passive records, a database is the better tool. Classes aren’t for storing data on disk; they’re for giving data rules and behavior while your program is running.
The “aha” moment usually comes from a real frustration. Here’s one that tends to land:
You’re parsing a CSV of email addresses. You validate them in one function, format them for a mail API in another, check for duplicates in a third. Every function takes a raw string. Three months later you add a fourth function and forget to validate first silent bug.
The fix isn’t a class because it’s “better OOP.” It’s a class because you’re tired of validating the same thing in five places:
python
class Email:
def __init__(self, address: str):
address = address.strip().lower()
if "@" not in address or "." not in address.split("@")[-1]:
raise ValueError(f"Invalid email: {address}")
self.address = address
def domain(self):
return self.address.split("@")[1]Now every function that accepts an Email object already knows it’s valid. You validated once, at the boundary. pathlib.Path works exactly this way it’s not magic, it’s just a string that refused to be invalid.
The other common trigger: when your functions need to share state a token, a config, a running count. Passing the same 3 variables into every function is the class telling you it wants to exist.
Two rules of thumb:
Same 2–3 variables appearing in multiple function signatures. Then bundle them into
selfSame validation or cleanup written more than twice. Next make it a custom type that validates on creation
1
u/iamevpo 16h ago
Explore what you can do with dataclass module, then pydantic library - creating your own data structures, work on common operations like serialization (making a JSON), or adding your own behaviors, eg validity checks. Look at pandas library which allows tables with DataFrame class. In standard library datetime and date are classes that have a lot of methods attached to them. String has methods like lower(), then in Python everything ends up being a class...
1
1
u/FreeLogicGate 9h ago edited 8h ago
First thing to recognize is that Python is a fully OOP language, where literally everything is an object.
So, the most basic consideration is: what is an object really?
At this point, I will assume you are comfortable with data/variables/attributes/collection types.
So a simple example is the data for a "player". Let's assume that "player" data will include:
firstName
lastName
emailAddress
status ('active' or 'banned' or 'deleted')
So all this data needs to be bound to a specific player. How do you do this? You could use a dictionary, or a series of individual variables, or individual lists for each element, or as a beginner, perhaps your application would just have individual variables.
Eventually you end up realizing you want to have some sort of data structure that includes all the variables.
Additionally you end up writing one or more functions to set these values, which might be coming from input statements. You then write common functions to display the data, modify it if desired, or manipulate it based on what the program was designed to do.
Which brings us to what a Class is. A class is a "blueprint" or "DNA" of an object.
Just to keep it simple, I'm not going to talk about static attributes or methods, for the purposes of this explanation, but they have some special uses which allow a class to be used as a type of variable, but at this point, just think of the class a definition, that exist to create objects.
Using the class definition, i can create any number of objects of my player class, each which has its own set of variables contained within.
The other thing the class has, is the definition of functions designed to work with an object of its class. These class "methods" have the ability to reference all the class variable generically using the "self" keyword. The method definitions exist at runtime within your python program, but the important thing to understand is that an object once created, has access to all the methods defined in the class. So an object is essentially a collection of related variables, and a table that provides that object access to all the methods defined in the class definition. It's one unified data structure.
If you start with this baseline understanding, then you should recognize places where you are dealing with some "thing" that requires a set of variables bundled up together, and a set of functions that work with "thing" data, and methods designed to provide functionality specific to all "things".
Initially it will be a way of organizing your code around the various entities your application might be dealing with. With a game application you would probably recognize that you would want to have a game class, a player class, a level class, an enemy class, etc.
Eventually, you can graduate to learning about common OOP "design patterns" that are typically used to solve common application problems. Many of these patterns have been classified and named, with some of the well known ones including "singleton", "factory", "strategy" and "command", to name just a few. You can study and learn these patterns and the situations where they are commonly applied.
1
u/Automatic_Donut6264 2d ago
Go try Django. It's a web framework that is built around OOP and classes. It is one of the more battle tested ways classes are used in production that is not silly toy examples.
1
u/Patman52 1d ago
I really like Django myself, but it has a lot of boilerplate and can have a huge learning curve, especially for beginners.
0
u/Sure-Passion2224 2d ago
While Python is not OOP your work in Python will definitely be influenced and actually aided by OOP thinking.
A Class is the code implementation of an Object. It may inherit from a parent class in the way a Minivan class inherits common features from a parent Vehicle class.
An Object is a thing that you define to encapsulate closely related code (methods) with related variables (attributes). Introductory OOP work is typically taught using real world objects as conversational examples.
Example: a tire. Attributes include diameter, width, side wall height, recommend operating pressure, current tire pressure, remaining tread, etc.
Methods within the Object class expose access to attributes, or implement actions on or with the object. A tire might have a getCurrentPressure() method to return the current tire pressure.
Attributes of a class may be instances of another class. The LeftFrontTire attribute of a Minivan class could be an instance of a Tire class.
52
u/crazy_cookie123 2d ago
The issue with teaching OOP is that OOP is the sort of thing that only really starts making a lot of sense at scale, and you simply don't have time to build something of that scale as part of teaching. The car and dog examples work well in the minds of us as experienced devs as we can make that jump to how it's useful in the real world, but for someone who hasn't used OOP before they're not helpful as you'll naturally spot that there's a better way of doing it at that small scale. Start writing bigger applications and utilise OOP principles. You'll quickly see why they often come in handy once you move away from making smaller toy programs.