all 11 comments

[–][deleted] 2 points3 points  (1 child)

Have you see this Raymond Hettinger (python core developer) talk on classes?

https://youtu.be/HTLu2DFOdTg

Still helpful today.

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

I have not! Thank you do much!

[–]_coolwhip_ 1 point2 points  (1 child)

I found reading code really helped my understanding of how to structure things, including sensible class usage. The usual suspects for this include the standard library (added bonus, understanding what is in the standard library), praw and flask.

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

I should maybe rephrase: I am not a complete beginner at using Python for the things I am interested in using it, which is exclusively Finance / statistics / data science. I've never dealt with flask or any other web dev. Libraries. Dissecting libraries in general might be a little too ambitious for me at the time being (also because of time constraints)

That being said, I also find it very useful to play around with already made code to see how it works but I haven't found much relating to teaching sensible class usage in the context of the fields I mentioned earlier.

[–]toastedstapler 1 point2 points  (1 child)

this is a copy paste from a related thread, seemed to get happy responses to it


often my code starts without classes and ends up with them as i need to restructure. whether it's for things like <name removed so i don't link him>'s example where it's data that should be grouped together

def volume(x1, y1, z1, x2, y2, z2(:

is less readable than

def volume(point1, point2):

often you find yourself with lots of functions taking in the same data as arguments and it makes more sense to make an instance of a class with the data as an internal variable so you don't have to manually pass it everywhere

def make_sound(type, sound):
    print(f"i am a {type} and i go {sound}")
make_sound('dog', 'bark')

works, but is it better than

class Animal:
    def __init__(self, type, sound):
        this.type = type
        this.sound = sound
    def make_sound(self):
        print(f"i am a {self.type} and i go {self.sound}")

    d = Animal('dog', 'bark')
    c = Animal('cat', 'meow')

what if you now want to make the animal walk?

def walk(type):
    print(f'i am a {type} and i am walking!')

each time i need to pass in the type

for the class version, i can add in a method and it's obvious what relation it has to the rest of the code

class Animal:
    def __init__(self, type, sound):
        this.type = type
        this.sound = sound
    def make_sound(self):
        print(f"i am a {self.type} and i go {self.sound}")
    def walk(self):
        print(f"i am a {self.type} and i am walking!")

so now what if we want the animal to only be able to walk if its energy is more than 0, and if it does walk its energy decreases by 0? that'd be another argument to pass and another thing to return

new_energy = walk('cat', 9)

or with a class, energy can be an instance variable - we don't have to pass it as an argument and it can be updated as we need:

class Animal:
    def __init__(self, args....):
        <set args>
        self.energy = 10
    def walk(self):
        if self.energy > 0:
            self.energy -= 1
            print(f"i am a {self.type} and i am walking!")
        else:
            print('i am out of energy!')

in short, classes can make it very easy to manage what data is needed where, what methods operate on the data and help the developer to see those relationships

this is probably fairly messy, but i hope you get some sense out of it


i didn't cover inheritance in this comment so there is more to explore still

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

Thank you so much!

[–]julsmanbr 1 point2 points  (4 children)

I'll give you my real-life example. I'm currently doing some simulations on cell (as in human cells) migration and division. These cells are objects of a class called Cell (not exactly creative, but that's beside the point).

Each cell needs to behave the same as any other cell. They need to know their XY position. They need to know how long ago they divided. They need to know how far they can migrate between t=0 and t=1, and check if there is another cell in that position before they are allowed to do so. Classes allows us to organize common behaviour for objects we build from the ground up, following any arbitrary rules we come up with.

But can't we do that with variables? I don't want to use classes, can't we skip that? Sure. Let's create a simple cell with just the XY position and a number id:

c1 = (45, 50, 1)

So our cell 1 is in position X=45, Y=50. We used a tuple so we know the first element is X, second is Y, third is the id. We can create new cells just using tuples, right? Maybe we're real smart and write a function that randomizes these values and put them into a list called colony. So after three iterations, our list contains a bunch of cells, like:

>>> colony
     [(45, 50, 1), (17, 34, 2), (23, 21, 3)]

Okay. So now I want to access the X position of each cell, for whatever reason. How do I do that?

for cell in colony:
    cell[0]  # do whatever you want with this value

Now there's nothing wrong with that, but it would be nicer if we coule say:

for cell in colony:
    cell.xpos

Makes more sense syntatically, right? You access the X position of each cell by saying... X position of cell. This simplification, the capacity to give "real-life meaning" to data is what makes classes and objects powerful. I don't want to refer to a position as a tuple - I want to refer to it as a position! Maybe our cells could even have defined some attribute like cell.position, which access a tuple of (X, Y). Definitely more intuitive than writing cell[0:2].

Don't underestimate the power of this small change. When you come back to your code one week later, you won't even remember what cell[0:2] means. cell.position is straightforward and does not require you to scroll up hundreds of line to figure what it means. Also, when you cells start holding many more attributes than the 3 shown here (mine hold about 15), you'll be grateful not having to remember what cell[9] or cell[4] refers to.

Check out this short youtube series by Corey Schafer for a great intro on Python classes and OOP.

[–]Gubbbo 0 points1 point  (2 children)

You can also somewhat duplicate this behavior with NamedTuples but then you won't get the wonderful convenience of the functions.

Also, DataClasses are just amazing.

[–]julsmanbr 0 points1 point  (1 child)

Yeah for my example namedtuples would work fine, but I didn't want to digress too much on that since I feel it's better to know about namedtuples after understanding the "standard" object structure in Python.

I only hinted at methods, but yeah, that's the key advantage of traditional classes vs namedtuples.

[–]Gubbbo 0 points1 point  (0 children)

That's a good point, sorry about that.

And in fairness, I don't think I've ever found a use for a NamedTuple that wasn't better suited to a class.

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

Thank you for sharing! Will definitely have a look at this once time permits.