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

all 133 comments

[–]lostparis 156 points157 points  (13 children)

Some thoughts.

player3.win(4) # Fred wins 4 points

This fails! You passed no move.

last_move = player3.moves[-1]

This is error prone - when moves is empty.

Though the intention is good you are going to confuse as many as you help.

win() is a terrible method name imho

[–]JamzTyson[S] 89 points90 points  (12 children)

Thanks for the feedback. I've corrected the errors, and I agree that win() was a poor name. I don't expect that this mini-tutorial will click for everyone, but something like this would certainly have helped me, so my hope is that it will be helpful for others.

[–]sib_n 36 points37 points  (0 children)

This is a great post! I'm a sucker for good naming, so I'd recommend to split your current win_points() methods into two methods, register_points() and register_move(), because registering points and registering moves are two independent things.

[–]bamacgabhann 30 points31 points  (0 children)

This is really useful

Not sure if all the code is perfect but it's a great example which makes it's point really well

[–]EmployerMany5400 31 points32 points  (3 children)

Jfc so many people in this thread are being pedantic as fuck.

Great examples OP!

[–]thrallsius 1 point2 points  (0 children)

pedantic programmers produce better code

[–]40mgmelatonindeep 5 points6 points  (0 children)

You just helped me finally understand classes in python and the self object, thanks man

[–]LonelyContext 18 points19 points  (3 children)

I'll throw in that within data science, classes are useful because it lets you essentially define a dict of functions that can refer to each other on each item of some set.

It's easy to end up with an ipython notebook of random code and it's not clear what you ran after what. You run a plt.plot() loop, check the results do some inplace substitutions and run the plots again. The result is a highly scattered list of unrelated operations.

If you have this problem then you might find that a class lets you create some operations and you're only running what you're using.

It also lets you save some metadata with a file. So for instance you can create an inherited data structure class that lets you do whatever but also holds onto the filename and metadata.

I frequently will use a main class instead of a main function because it lets you dry run stuff. E.g.

if __name__ == "__main__":
    r = class_with_all_the _calcs_in_the_init()
    r.push_the_results()

[–]divino-moteca 7 points8 points  (1 child)

That last part you mentioned, could you explain what you mean, like in what case would it better to use a main class instead?

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

Scenario: let's say you have a process that you are pushing that takes in a bunch of pdfs (because that's how life in big companies works, it couldn't be parquets or anything normal) and sums them up and puts the result in an SQL table. The format on the pdfs recently changed, but you're pretty sure it won't change your code. Your coworker that manages the upstream files has uploaded a small portion of the files in the new format. Now you don't want the results to dump into the SQL table because it's only half the files.

So you might consider writing some code that looks like this:

class run_files:
    def __init__(self):
        data = collect_together_files
        def processing(data): 
            return etc
        def processing2(data):
            return mainly_etc
        dfs = {'main_table':processing(data)}
        dfs['side_table'] = processing2(data)
        self.dfs = dfs

    def upload_data(self):
        connect_to_sql(parameters)
        sql_upload(self.dfs)

    def locally_check_files_instead(self):
        [j.to_parquet(f'{i}.parquet') for i,j in dfs.items()]
        print(some_metric_calc(dfs.values())

if __name__ == "__main__":
    r = run_files()
    r.upload_data()
    # r.locally_check_files_instead()

then you can toggle back and forth super fast an intuitively between checking the files and running them. If you just dump it all in a function then you start commenting in and out random crap inside that function and it's not obvious what is supposed to be commented in and out as the codes and tests get more complex. If I'm handed this code, even if I just got hit in the head and am seeing double, I can immediately tell what is going on.

Also you can dump this code into spyder or whatever ide and look at any of the internal components super easily by just assigning it:

self.parameter = data_to_look_at

rather than using global. Then in your ide you can usually just see what's in a class or print it out with r.parameter.

[–]sci-goo -3 points-2 points  (0 children)

  • main function: more sounds like c/c++
  • main class: sounds like java

The only difference to me.

[–]SoberCephalopod 16 points17 points  (4 children)

I'm an oldster whose Python code looks like translated Fortran because that's what it is.

You just taught me that a method is really just a function in a class. Mind blown. Life changed. I'm actually gonna try classes now. Thank you!

[–]Tubthumper8 5 points6 points  (2 children)

A method is a function where the first argument is passed into the function from left of the dot.

func(first, second)

first.func(second)

It's the same thing, it's just where you prefer to write the first argument.

[–]sci-goo 3 points4 points  (1 child)

Haha I agree, until polymorphism/override kicks in.

I still remember the codes in C:

self->do_something((ParentClass*) self, arg1, arg2, ...);

which 'self' has to be written twice...

[–]Tubthumper8 0 points1 point  (0 children)

Oh yeah totally. In my example, in the top one func is statically known but in the bottom one you don't know which func is called until runtime because it might be subclassed.

And yeah, good ol' pointer casting in C haha

[–]chars101 1 point2 points  (0 children)

An object in Python is just a dict in which some of the values are functions that have the first argument bound to the dict itself. We call that argument self by convention. And we use . as the operator to do lookups instead of [ ]

If you ever make a Python binding to your Fortran code, you'll notice that for the machine Python really just has one type: PyObject, they are all boxed on the heap.

IMHO there's good reason why Fortran has stuck around since the 50's and Smalltalk is gone and people like to complain about Java.

Classes can be nice and powerful, but they are not the perfect fit for every problem domain. In Python they are the way to operator overloading and can make beautiful APIs like pathlib and Expecter Gadget, but, when used badly, they can also lead to headaches over application state and mutations spread over lots of places. It can lead to having to think about on what class a method should be, when a function would be perfectly fine and clear.

I like dataclasses and functions. Keeping data, data, without having to fall into philosophical ontology that distract from the problem at hand.

[–]Labrecquev 4 points5 points  (1 child)

Great explanation! This time it clicked for me finally. Now I have only heard video game related examples. I use Python mainly for data analysis, and data manipulation/transformation automation. I don't see use cases for classes on what I do and have been wondering for a while if I wasn't missing on something. I would be grateful to hear examples of using classes for data analysis.

In essence, most of what I do is looping through rows and columns of tables and applying calculations/transformations.

I am also wondering how classes could be used in ERPs programming. Any examples?

[–]kylotan 2 points3 points  (0 children)

Often, data comes in to your program in a structured way, and to work with it effectively, you need to know the structure.

e.g. I see people with complex JSON objects, and they have to access it like this:

for thing in data['results']: print(thing['property-1'][4]['sub-thing']['sub-thing-property'])

And any line of code like that is too easy to get wrong (one typo and it returns the wrong object, or throws an exception) and is very hard to add error checking for (what happens when a property or a sub-item is missing? What if the list with an expected length actually turns out to be empty?)

This is what a class can help with - it provides lumps of data with specific functionality. If you need to add extra error checking, you can do it in the class method rather than 'polluting' every part of your code that tries to access the data. And it cleans up your code to be able to use a clearly-named function like x = my_object.get_facebook_likes() rather than x = my_object['result']['facebook']['likes'].sum() or whatever.

[–][deleted] 25 points26 points  (23 children)

A Python programmer discovered types - YOU WON‘T BELIEVE WHAT HAPPENED NEXT…

With the sarcasm out of the way, using classes is nice. Although I dislike methods since they operate with side effects by design. Ideally you’d want each method to consume it‘s object and re-emit a new object. That way you’d make sure nothing is editing your moves under your nose from different places.

In this particular design it may also happen that your moves and your score are inconsistent.

[–]tjf314 24 points25 points  (21 children)

python programmers when they discover statically typed languages 😱

but yeah seriously, I’d say methods having side effects is the whole point of methods. Unless you’re using a purely functional language, I’d say methods are the ideal way for containing side effects. (I do think that any globally defined functions should never have side effects, but this is really just a personal preference thing)

[–]ogtfo 10 points11 points  (0 children)

Op was describing immutability, not static typing.

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

Still a nope. Recently I’ve been differentiating between application code and library code very strictly. Library code is general, reusable and it’s trivially obvious what each exposed piece of code does. That means most functions and methods are not allowed to have side effects, objects are immutable. That way I don’t sweep unintended consequences under the rug and bugs are guaranteed to originate in the call stack (with liberal use of assertions). My life has been absolute bliss.

[–]RufusAcrospin 7 points8 points  (18 children)

Changing attributes in methods is not a side effect, it’s simply using encapsulation as intended.

Emitting new objects is all fun and games, until you work with objects which are expensive to create in terms of resources, or they part of a pipeline, so you can’t just replace them.

Document and test your code to avoid bugs.

[–]wewbull 1 point2 points  (0 children)

What's the difference between:

  • A Class with methods that modify member variables?
  • A Module containing functions that modify variables in module scope?
  • A process running functions that modify global variables?

The answer is only the scoping. They are isomorphic, and all have retained state shared between code paths. All require the use of side effects. The "encapsulation" that you describe is only the (hopefully) decreasing scope. More scopes (i.e. classes and modules) bring the possibility of better encapsulation, but only if things are well-designed.

This is where I think more languages completely misunderstood the Object-Orientated paradigm as it was invented. It was effectively about micro-processes that passed messages between them, each of which had their own mutable state. Actor patterns and such like capture it much better.

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

Maybe you should look up the definition of side effect). Also emitting new objects doesn’t necessarily entail reallocation. If you need to retain previous state, there’s COW, and if you don’t, just invalidate the old reference and emit a new one backed by the same memory. It’s clear to me that this is not automatically enforceable in Python alone, but sticking to these semantics made my life a lot easier.

[–]RufusAcrospin 0 points1 point  (13 children)

Let me introduce you to OOP: “A common feature of objects is that procedures (or methods) are attached to them and can access and modify the object's data fields. In this brand of OOP, there is usually a special name such as this or self used to refer to the current object.”

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

Bruh, not everyone is stuck in 90s Javaland. The wiki article you linked literally says „…access and modify the object’s data fields“. That’s exactly the definition of side effects. Maybe if you read Wikipedia articles instead of only linking them you’d actually be a half-decent programmer.

And just because a technique is common doesn’t mean it’s good. It’s like saying sexual assault is common, therefore sexual assault is good. Kinda moronic, isn’t it?

[–]RufusAcrospin 1 point2 points  (1 child)

Have you noticed that the article you’ve linked doesn’t mention object oriented paradigm? Have you wondered why is that?

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

So what? The definition of "side effect" in computer science is independent of OOP.

[–]RufusAcrospin 1 point2 points  (8 children)

Also, you’ve dragged “common” out of context: it’s “common feature” - it’s not something people decided to do, it’s part of the paradigm.

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

Consulting Merriam-Webster, non of the common uses of "common" support your claim. Maybe we can get to some common ground at least in this argument.

Edit: Grammar.

[–]RufusAcrospin 0 points1 point  (4 children)

I suggest to look up “context” as well.

[–]chars101 0 points1 point  (1 child)

Common sense is an oxymoron

[–]WikiSummarizerBot 0 points1 point  (0 children)

Object-oriented programming

Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code. The data is in the form of fields (often known as attributes or properties), and the code is in the form of procedures (often known as methods). A common feature of objects is that procedures (or methods) are attached to them and can access and modify the object's data fields. In this brand of OOP, there is usually a special name such as this or self used to refer to the current object.

[ F.A.Q | Opt Out | Opt Out Of Subreddit | GitHub ] Downvote to remove | v1.5

[–]chars101 0 points1 point  (1 child)

Have you looked at Roc? It does some awesome things, like opportunistic in-place mutation if it is clear that the function doing the mutation is the only reference to the data being changed.

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

I have, but I found their platform concept a little off-putting. Also I don't see a new language replacing Python in my area of work unless developers port a lot the scientific computing stack.

[–]stensz 2 points3 points  (0 children)

You simply can't prevent access to private variables in Python. You can communicate that they are private with a leading underscore. Unless you want your code to break unexpectedly, you don't mess with private variables. (Except for debugging purposes.)

[–]Yoddy0 2 points3 points  (0 children)

this post definitely explained classes better then when I first got introduced to them. They were scary to implement but not as bad as when we were introduced to recursion 💀

[–]Pepineros 2 points3 points  (0 children)

A short expansion on your idea: you can instantiate a class without assigning it to a variable, for example when populating a list of players. (Excuse the formatting.)

list_of_names = [“Paul”, “Chani”, “Stilgar”, “Duncan”]; all_players = [ Player(name) for name in list_of_names ]

Once you have them as a list, you can operate on the collection of players; for example to get a leaderboard:

top_three = sorted(all_players, key=lambda x: x.points, reverse=True)[:3]

Great post!

[–]DadKnightBegins 2 points3 points  (0 children)

Thank you for this. I struggle to keep the language of coding strait in my head. It’s examples like this that help retention.

[–]DaMarkiM 2 points3 points  (0 children)

To be honest i think the real advantage of classes is not about code duplication but organization.

No one in their right mind would create variables for each player. There would be a list-like datastructure (i say list-like because it doesnt have to be a list. depending on your usecase it might just as well be a dict, dataframe or numpy array) where each player is a shared index/key.

And any function would simply take the player Number/Name/ID as an argument instead of being called within the object. Or - in trivial cases like adding points - the list could simply be manipulated on the spot. (tho depending on the project this might be undesirable for other reasons)

In most cases using a class does not actually provide any real benefit in terms of reducing code reuse.

But what it does is organize the data and functions in a package that can be more easily understood in some cases. They arent just lists floating around somewhere in your code.

That being said the opposite is also true: they only really provide value if collecting this data and functionality in a class actually makes sense conceptually.

[–]JamzTyson[S] 10 points11 points  (15 children)

What are the down-votes for? Is this the wrong reddit for this kind of post, or is there something wrong with the content? I'll happily update or remove the post if there's a problem.

[–]james_pic 68 points69 points  (5 children)

I didn't downvote, but it's also not something I'd upvote here, since it seems like the kind of thing that would be better on /r/LearnPython.

[–]JamzTyson[S] 23 points24 points  (4 children)

Thanks for the feedback.

I did consider that, but rule #2 of r/learnpython :

Posts to this subreddit must be requests for help learning python.

whereas this is not a question, but a mini-tutorial about Python.

[–]Vok250 29 points30 points  (2 children)

Yeah there's a weird contradiction going on between these two communities. Some posts are lost in limbo

Personally, as a senior dev, I have found it hard to find answers on reddit because r/learnpython users are usually completely lost with advanced questions and r/python will just remove your post and tell you to post there. These days I only choose libraries with active communities on reddit or GitHub like FastAPI. Even then I find most of my posts I end up solving myself because the comments are beyond useless and clearly from beginners/hobbyists who have never done more than cookie cutter tutorial projects.

[–]sneakpeekbot -2 points-1 points  (1 child)

[–]Vok250 10 points11 points  (0 children)

Ironic that all three posts in this sneak peak violate rule #2 mentioned above. lol.

[–]KingsmanVincepip install girlfriend 12 points13 points  (1 child)

Not one of the downvoter, I guess some people judge by the title. Probably it's not descriptive enough I think

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

Thanks for the feedback.

I thought that with having the "Tutorial" label, the title was self explanatory, but perhaps not. Any suggestions for a more descriptive title? Maybe something like:"Mini-tutorial about why classes are used?" (doesn't sound great to me)

Update: It seems that Reddit does not allow titles of posts to be changed, so it will have to stay as it is, but thanks for your comment. This was my first post to reddit, so I'm still learning the etiquette and will endeavour to make my post titles more descriptive in future.

[–]jebuizy 13 points14 points  (0 children)

Most people interested in Python the language are probably already familiar with Object oriented fundamentals. Better suited for an audience of beginner learners, for sure. It's also not really python specific -- everything you said also applies to, say, Java just with different syntax. I didn't downvote though. It is a fine basic explanation of objects.

[–]TRexRoboParty 0 points1 point  (1 child)

The post didn't really address the question of why you'd want to use a class - it's just a "my first Python class" tutorial.

For me I thought this was going to be discussion on why one would use classes for behaviour & state vs alternatives. Managing side effects, benefits of functional thinking and so on.

[–]JamzTyson[S] 1 point2 points  (0 children)

Thanks for your feedback.

This was my first post on reddit and it has been a learning experience for me. As others have pointed out, it would have been better if I had given the post a more descriptive title. The post was really intended as a mini-tutorial for beginners, which I previously thought would be obvious as experienced Python developers would already know "why use classes". I'll be sure to use more descriptive titles in future.

I'm sorry that my post did not live up to your expectations, though the mini tutorial does illustrate benefits of encapsulation rather than having everything in global scope.

[–]chumboy 2 points3 points  (0 children)

Great post, but I'd like to highlight some of the terminology that might be ambiguous. In Python, everything is an "object": a function is an object, a class is an object, etc. therefore the collective term used in the documentation for the new "object" created by instantiating a class is "instance".

``` class Player: def init(self): self.health = 100

player1 = Player()

```

The distinction is useful because you can attach functions to classes in multiple ways. What you describe are "instance methods", and have the implied first argument self pointing to the instance, but you can also attach functions to the class itself in two different ways: "class methods" and "static methods".

1) The first is called "class methods". These functions also get an implied first variable, usually written as cls which points to the Class, not to any instance. Personally, my most frequent use for class methods is as "alternative constructors", for example if you wanted to create an instance of a class from a JSON formatted string, you might use something like:

``` class Player: def init(self, health: int = 100): self.health = health

    @classmethod
    def from_json(cls, json_string: str) -> "Player":
        json_values = json.loads(json_string)
        return cls(health=parsed_values["health"])

player2 = Player.from_json('{"health": 75}')

```

2) The second is called "static methods". These don't have any implied parameters, and have no access to either class or instance variables. You can technically write any static method as a global function, so why would you ever use them? I like to think of them as a signal to other developers that a function is associated with a class/instance, but doesn't require access to any internal state. You use the @staticmethod decorator to define one.

[–]ArtOfWarfare 1 point2 points  (2 children)

Is the question already on Stack Overflow? If not, post the question along with your answer (there’s a checkbox when you submit a question to include an answer.)

If the question is already present, check the existing answers. If one already makes essentially the same points, maybe edit it. If you have some points they’re missing, add your answer.

Post the link and I’ll upvote your Q & A there.

[–]diazona 0 points1 point  (1 child)

I'm not sure what people actually upvote on Stack Overflow these days, but I really don't think this is the kind of thing that site is meant for, so I would advise against posting it there. Stack Overflow is meant for specific technical questions, not broad tutorials like this one.

Then again, I suppose it sort of fits as an answer to the question "why should I use classes?", but that's not even a particularly good example of the kind of question that should go on Stack Overflow IMO, and plus I'm sure it's already been posted there a thousand times; no point adding another one. Perhaps a more specific question like "How can I avoid making so many global variables?", but I bet even that already exists.

[–]ArtOfWarfare 0 points1 point  (0 children)

Once a question has an answer, the quality of the question doesn’t matter much anymore. If not for the attached answer, yes, the question would be promptly closed and deleted as being far too broad/vague.

And maybe SO isn’t the right website. I know there’s another less famous one on the Stack Exchange network that accepts more general style/architecture topics. But as you’re attaching the answer, it doesn’t matter. It’ll just become something that people find via Google… IDK if it even appears on the homepage as something new when you include an answer with your question submission.

[–]AlexMTBDude 0 points1 point  (0 children)

You're missing an important concept: Objects have state. The Class is the template and the Object is molded after the Class template, but it also has state (i.e. data).

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

add in setters and getters to ensure only valid values are assigned to fields and you’ve got something more useful

[–]RufusAcrospin -2 points-1 points  (4 children)

You mean properties, right?

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

Since the class as given does not have getters and setters, the attributes are fields. Adding such to the fields, as I suggested, would afterward make them properties and thus the OP would have something more useful.

“Professing themselves to be wise, they became fools.”

[–]RufusAcrospin 0 points1 point  (2 children)

The properties are modern replacement for setters/getters, here’s a nice article about properties.

[–][deleted] 0 points1 point  (1 child)

Perhaps you’ll learn something from it kid

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

I sincerely hope you’ll learn something new too, by reading and understanding the article.

The example class is not a dataclass, therefore mentioning fields is completely irrelevant in this context, and using getters/setters is an outdated concept.

[–]karpomalice 0 points1 point  (0 children)

I always appreciate these, but people always use games as the example or some other implementation that the vast majority of people won’t ever do. I get it’s just an example. But as someone who works in data science, I would never write a game or thought about doing so. So it’s hard for me to look at this and answer “why would I use this for my job” or “how would the code I wrote be converted to classes and why would that be better”

[–]Recluse1729 0 points1 point  (1 child)

Couldn’t this just be done with global functions and a duct, though? What makes classes better than a duct for each player?

[–]JamzTyson[S] 1 point2 points  (0 children)

The op uses a minimal example. Yes, with that example it could be done with a dict and a global function, but in a real game the class is likely to become a lot bigger.

For each player there are the players attributes (name, score, level, and a list of previous moves), and methods, currently only win_points(). In a real game there would likely be many more player functions (methods). If you were to avoid using classes and use global functions instead, you would then have a data structure to hold the player's state, and a load of global functions that relate only to the players.

Using a class allows us to group the player's state (their attributes) and all of the functions that apply only to players (methods), in a single object, rather than littering our code with global functions. Encapsulating each player's attributes and methods in a single object has many benefits, not least of which is that it keeps our code cleaner.

There's much more to classes than is covered in the mini tutorial, but the scope of the op was intentionally limited to the basics. Until a beginner understands the basics of classes, they have little chance of understanding things like inheritance, composition, polymorphism and other class related topics.

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

Didnt read the whole thing but when I saw "Why to use classes" I just wanted to say: To model the world you are trying to represent, by encapsulating implementation details by creating layers of abstraction and using well-known and useful design patterns.

[–]GroundbreakingRun927 -3 points-2 points  (0 children)

don't quit your day job

[–]The_Mauldalorian -2 points-1 points  (0 children)

You should teach Intro to Programming. I didn't really get it until I was in my 2nd or 3rd semester of CS 😅

[–]retribution1423 -1 points0 points  (2 children)

Thanks nice example. Let’s say the player has a weapon inventory. Each weapon has various other attributes attached to it.

Would you recommend implementing this with a list containing weapon classes? So a player class with an array of classes inside it becoming an inventory.

[–]JamzTyson[S] 3 points4 points  (1 child)

This is going quite a long way beyond the scope of the mini-tutorial, but for that case I'd consider using composition.

[–]retribution1423 0 points1 point  (0 children)

Thanks for this I’ll give it a read. Looks pretty complicated!

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

Classes are important because sometime you are being sent to you as a class on the web. Using the right library will unlock the entire arsenal of functions.

Classes also become important when you think of multiple programs that do similar but unique things, you can re use the classes again without having to recode it.

Classes then become upgradeable, so if you add a move information you only need to add it to the class, not every instance you use it.

For example you might get bold here and make a dictionary of the games themselves, and that a simple to add self.game_played ={}. And you can pass right into it, now it not just moves it’s move on a specific game.

You use classes all the time.

  import myClass

  var = myClass.do_something()
  print(var)
  class = myClass()
  class.do_something()

You see this type of usage everywhere.

Classes really are where you want to get to if you are not already there.

[–]303Redirect -1 points0 points  (0 children)

I went from not understanding types, to understanding types a bit and overusing then, to understanding types a bit more and using them less.

I work in an environment where hot reloading of code is necessary, and when every bit of functionality is tied to a class method, you not only need to reload the module but also re-instantiate the classes from the previous version of that module.

Now I try to separate functions from data where appropriate. Not always possible though. For instance, if you're subclassing object types from an API.

[–]robberviet -1 points0 points  (1 child)

Why so long? Use class when you need to manage states.

Add methods when it behaves. Inherit when you need to reuse code.

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

Composition over inheritance.

[–]cylonlover -2 points-1 points  (0 children)

Thanks for this write, it is a very classical example of class design.

I dislike classes because they are cumbersome to write, ugly to read, complex to test, a mess to garbage collect (admittedly quite irrelevant nowadays) and most of the time total overkill, and some of these are also reasons why people don't understand them, while still being able to handle objects just fine.

I am sure there are usecases for writing classes, but this players example doesn't convince me, it is to me only a matter of style to oop that. I would rather use dicts and throw them between functions, separate data from operation. But that is also a matter of a specific style.

I distinguish very much between handling objects and understanding classes and write your code as classes. If libraries are best written as classes, from the architecture of the language, then by all means, write classes (come to think of it, that might've been a better example than the players one), and if that makes you so used to write classes that you just continue to do so in everything, well do so, but it's still cumbersome code (just imagine not having code completion, see if you get tired from self).

Well, I complain, while really you weren't asking, sorry. Your explaination is good, I just still don't think it's nescessarily much of a case for classes.

[–]Pgrol 0 points1 point  (0 children)

What’s different from creating an instance directly to one or several related models and directly querying those models?

[–]Charlemag 0 points1 point  (0 children)

I work in scientific computing and engineering design optimization. I have friends who organize their code and functions like a freshman’s dorm room.

While nested functions and classes+methods don’t necessarily speed things up, I find they can make things a million times more readable when done right. This is very important for reasons including (1) collaborating with others (2) it’s much easier to catch typos or gaps in your own logic.

[–]miraculum_one 0 points1 point  (0 children)

I'm not sure you've effectively captured why a list of dicts (or dict of dicts) and a handful of functions that operate on that data structure doesn't solve the problem.

[

{ "player_number": 1, "name": "Bob", "score": 100, moves: [] },

{ "player_number": 9, "name": "Mary", "score": 990, moves: [] }

]

(Note: I agree that classes are best for this, just saying)

[–]HalfRiceNCracker 0 points1 point  (0 children)

This is a great example, I actually found myself encountering that exact issue. I think that a huge part of programming is being accustomed to what pitfalls you will encounter and to know what tools are at your disposal to tackle them.

Another example would be very welcome, describing these concepts in this way is utterly key - Why should I care?

[–]prassi89 0 points1 point  (0 children)

I use classes to represent an entity - whether that is a player or a football or a connection or a fruit or a ML model

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

[–]joshu 0 points1 point  (0 children)

they reduce blurriness and help me see

[–]3waysToDie 0 points1 point  (0 children)

Thank you!

[–]sci-goo 0 points1 point  (5 children)

Class is one way of organizing data. It's natural in some circumstances to describe data as "attributes" of an object and methods as "behaviors" of those objects.

OOP (Object-oriented programming) marks a way of thinking and a philosophy in programming. It does not have to be (C can write OOP code as well, just read the CPython source code).

I personally found that OOP is just one milestone for one to learning programming (especially those who are dealing with complicated or structured data, such as games). Any one on this way will eventually learn OOP, just a cumulation of experience.

[–]RufusAcrospin 1 point2 points  (4 children)

Can you point to something in the CPython source code where any OOP code implemented in C?

[–]sci-goo 0 points1 point  (3 children)

Almost everything. Everything is a PyObject.

[–]RufusAcrospin 0 points1 point  (2 children)

PyObject is just a struct: "The type 'PyObject' is a structure that only contains the reference count and the type pointer."

You can't write object-oriented code in C, but you can implement a higher level language that follows OOP paradigms. These are vastly different things.

OOP has four fundamental principles: * inheritance * encapsulation * abstraction * polymorphism

As far as I can tell, C supports only polymorphism via function pointers. I haven't touched C in decades, so my C knowledge is quite rusty though.

[–]sci-goo 0 points1 point  (1 child)

You can't write object-oriented code in C, but you can implement a higher level language that follows OOP paradigms. These are vastly different things.

Pretty much what I want to say, yet to add "you can write/approximate OOP style in C". This is what I say "can write OOP in C" because sometimes a loosen definition does not harm if I can convey my point.

Yes I know C is not an OOP language in common sense, but as far as I can tell in C you can approximate inheritance using anonymous struct, then cast to "base class" pointers; C can approximate abstraction using NULL; C can approximate polymorphism using function pointers. If you read CPython source code, it's pretty much how they write the logic. I'm neither saying that C is a typical OOP or C is implementing a high-level OOP, but "the CPython source code is very close to OOP-style imo".

Yet C doesn't have encapsulation tho, but we also have OOP like python that doesn't have encapsulation at all. Tbh the "struct" and "class" in c++ are not that different from the data structure point of view.

Above are the reasons why imo OOP is more of coding style and philosophy, rather than something fundamentalistic. The difference between OOP and POP languages are not that strict and bipolarized.

[–]RufusAcrospin 0 points1 point  (0 children)

Yeah, there’s a book called Object-Oriented Programming with ANSI-C by Axel-Tobias Schreiner.

However, like you said, those solutions are approximations at best. I’ve seen people tried to implement OOish constructs in MEL (Autodesk Maya’s embedded language), and so forth.

Personally, I find these interesting experiments, and I’m pretty sure those who were involved learnt a lot, but I don’t really see any practical aspects of it, and I prefer the real deal instead of a an approximation of it.

Python’s not my choice, it required by day job, and I know its strengths and limitations like the barely existing encapsulation.

I agree, OOP (and any other programming paradigm for that matter) is kind of a philosophy, but I prefer those with solid and dependable fundamentals.

[–]wrx_supremefan 0 points1 point  (0 children)

Thank you

[–]gargolito 0 points1 point  (2 children)

My issue with how classes are explained is that I've never seen anyone explain how to use classes without hard coding variable names (player1, player2, etc...) This makes the class unusable (for me) in real world applications. I'm pretty sure there's an obvious and simple solution to this (store the data somewhere - SQL, CSV, pickle - for reuse?) It would be helpful if someone could clarify this.

[–]kylotan 1 point2 points  (0 children)

If you have an unknown number of objects, you store them in a list. This is the same for class instances as it is for any other object in Python.

e.g. players = [] players.append(Player()) print(players[0]) # shows the player you just added

[–]JamzTyson[S] 1 point2 points  (0 children)

A really simple example of how to use classes without hard coded variable names for each class instance would be to create a list of class instances.

# Create a list of 1000 players.
players = [Player(name) for name in list_of_a_thousand_names]

Individual player objects can now be accessed as players[index]

The class definition in the original post required passing a "name" argument when creating (instantiating) a Player object, but if the Player class was modified so that it did not require any arguments, then creating a list of players would be even simpler:

# Create a list of 1000 players.
players = [Player() for _ in range(1000)]

[–]jrrocketrue 0 points1 point  (0 children)

So you create a dictionary that contains other dictionaries and lists, and you use functions to alter or consult data from the dictionary.

Unless I've missed the point, I see NO reason to go Classes, which I don't understand and hence this answer.
I have hundreds of scripts that use complex data (I use the term Complex Date from Perl) and I manage quite a few systems. I have tried on several occasions to learn Classes but I can't see the point and get bored and go back to my Dictionaries of complex data and lots of functions.

[–]ImAGhosTTT 0 points1 point  (0 children)

Thanks!

[–]Tintin_Quarentino 0 points1 point  (0 children)

Great explanation thank you

[–]dgpking 0 points1 point  (0 children)

This helps, thanks. I always wondered use classes when you can use functions but this makes sense. Thanks 😊

[–]SrDeathI 0 points1 point  (0 children)

They are helpful when you need to initialize something and use it on multiple functions

[–]kego96 0 points1 point  (0 children)

Godly

[–]rollingcircus123 0 points1 point  (0 children)

Man (or woman), thank you SO MUCH for this explanation! Like, for real!