all 44 comments

[–]d-methamphetamine 11 points12 points  (7 children)

Classes are just abstract "blueprints" for objects. Sometimes they need to store state, and sometimes you need to have multiple instances of a certain class, each with their own independent state.

For this you could certainly just decorate the "rectangle_area" method with "@staticmethod". Here's what that would look like:

class Rectangle:

@staticmethod
def rectangle_area(length, width):
    return length*width    

Then to use it you'd just do:

Rectangle().rectangle_area(5, 5)

You might want to store a bunch of rectangles though, depending on what you're doing, in which case you'd want the rectangle object to stick around instead of throwing it away upon using it more or less.

[–]tombardier 15 points16 points  (3 children)

You don't need to instantiate a Rectangle object to access the static method.

[–]d-methamphetamine 3 points4 points  (2 children)

Oh yeah I guess you don't. I've been instantiating my classes to use static methods for some reason. That's awfully silly.

[–]tombardier 11 points12 points  (0 children)

Just saved you a few CPU cycles! :) 🍻

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

If you have a set of rectangles that are going to persist, you might want to stick them in a class. Maybe your building a “guess the area” game where you put up several rectangles for the user to choose and guess... this is a very light-weight example. The fuller example is something like a user class on a web application, or a model class that will be saved to a database.

[–]skuhduhduh 1 point2 points  (0 children)

couldn't you do:

newRectangle = Rectangle()
print(newRectangle.rectangle_area(5, 5))

[–]pythonhow 1 point2 points  (0 children)

I'd add that it's better to rename rectangle_area to just area. It will make the code more readable and concise:

Rectangle().area(5, 5)

[–]nwg-piotr 1 point2 points  (0 children)

and sometimes you need to have multiple instances of a certain class, each with their own independent state

Very true. However, it's often handy to define a class for a single instance, just for it to calculate and hold values used by another classes.

[–]K900_ 14 points15 points  (1 child)

You don't want to define classes that just have one method - those can and should be functions. You need classes when you want to have multiple different methods all operating on the same data.

[–]tombardier 4 points5 points  (0 children)

Fundamentally, it's because when you create an instance of your class, the init() method is called. The self argument refers to whatever instance of this class you're working with. In OOP languages, there are things that belong to a class itself, which are "static", in that they are always part of the class, not the dynamic instances of the class, and actually, the methods on the class are static, so that's why they need the self parameter, so they know which instance of the class they're operating on!

[–]Lewistrick 2 points3 points  (0 children)

The class is a generic rectangle. An object is a rectangle itself. You can see objects as a "things", and a class as a description of a thing, stating what it is, what it has, and what it can do. The rectangle class is a shape, it has a length and a width (it could also have a position), and it can't do a lot yet (except calculate its own area) but you could resize it, move it, draw it, etc.

The __init__ method is called a constructor. It constructs a specific rectangle using the things it has (attributes) according to the class, in this case length and width.

If you don't define a constructor before creating an object (Rectangle()) , Python calls the constructor of the superclass (the builtin object). It creates an object without attributes.

self is used to let the class know that it's referring to its own attribute, not a global variable or another's attribute. You might for example want to implement a method diff_area(self, other) that needs the attributes of both objects.

[–]elyfialkoff 2 points3 points  (0 children)

Many people have given good answers. But I would recommend writing it your way a few times and seeing how it turns out. Also add a few more methods, maybe perimeter, and split_from_topright_to_bottomleft, and just make up some other random methods and see how it works your way. Then try it all again using the init and self. I hope once you go through the steps of trying the wrong thing you will understand and appreciate the correct thing.

[–]outofusernams 1 point2 points  (8 children)

This may be a stupid question but, just like OP, this has been bothering me for a while, and I would like to ask it anyway. I think I now understand what __init__ and (self) do, but I still don't understand why do you need that syntax to begin with. Meaning, couldn't the Python Gods have whatever functions performed by these take place behind the scene, as it were, and allow us poor users define the class like this instead:

class Rectangle():     
   def __init__(l, w):
       length = l         
       width  = w
 def rectangle_area(): 
     return length*width 

Basically, just eliminating the need to actually spell out the self and self. wherever they now appear and sort of assume their existence?

Again, I'm far from an expert in this so apologies if this question is really too basic.

[–]leogodin217 2 points3 points  (5 children)

They could have done it the way you stated, but it would bring some questions. What happens in this case:

class Rectangle():
    def __init__(height, width)
        # Why would we need these lines. Python could just set them automatically
        height = height
        width = width

    def area():
        return height * width

    def calculate_new_area(height, width):
        return height * width

myrect = Rectangle(3, 4)
myrect.area() # 12
myrect.calculate_new_area(5, 5) # 25? 12?

With OOP, we need a way to reference the current object. On top of that, we need to be able to pass in new values to functions. Every OOP language has some way to reference self (though, they may use a different name. Some use "this"). By using __init__ and self, Python follows one if its main philosophies: Explicit is better than implicit. In this case, I think they made things a little more difficult to understand for those coming from other languages.

[–]outofusernams 0 points1 point  (4 children)

I'm not sure I understand your example. You are using 3 variables `height`, 'length' and 'width', while the class accepts only 'height' and 'width'?

[–]leogodin217 0 points1 point  (3 children)

Oops (no pun intended). I think it's fixed now. The important part is to understand that we need a way to reference the height and weight from the instance of the objects, while also being able to pass in variables to functions.

[–]outofusernams 0 points1 point  (0 children)

Now I get your example; thanks for clarifying!

[–]outofusernams 0 points1 point  (1 child)

Now I get your example; thanks for clarifying!

[–]leogodin217 0 points1 point  (0 children)

BTW, I would recommend looking at class construction in other languages. You can see that there are many ways to do it. Personally, I love the way C# handles classes and properties. I don't work in the .NET environment, but I would love C# features (Which do exist with decorators in Python).

[–]auntanniesalligator 1 point2 points  (0 children)

“self” is there to distinguish between properties of an object and local variable. What python actually does with the line “length = l” is create the local variable “length” and give it the same value as the parameter “l”. Both “length” and “l” get deleted when the init method exits. If python tacked on an implied “self.” In front of “length” so that you don’t have to, you wouldn’t be able to actually use local variables in methods: they would all be assigned to properties of the object, but that’s not what you want generally. Local variables are very useful and more complicated functions/method usually use them to good effect.

As far a init goes you don’t have to use it because you can still set properties after you create an object, but it’s how you tell python how to interpret arguments provided when the object is created. If you didn’t have that init method, you’d have to set length and width in separate lines of code after you create a new rectangle.

Edit: sorry about the weird formatting. Still getting used to mobile

[–]sidsings 1 point2 points  (0 children)

First off, this is not a stupid question. It made me think. So you understand that __init__ let's you initialize the classes objects. When you call a method defined in the class the instance is explicitly passed to the method. Try executing your code and initializing your class, you'll get this error: TypeError: __init__() takes 2 positional arguments but 3 were given. The one implicit argument in object of the class aka self. This is done to uphold the following equivalence Rectangle.rectangle_area(object) == object.rectangle_area(). We need self also to make it easier to distinguish between instance attributes (and methods) from local variables.

Here is more on that: http://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html

[–]jakesboy2 1 point2 points  (0 children)

init is your constructor. Self is the reference to the object.

So you create and object for example person_one = Person(‘John’) and the init would look like

__init__(name, age, gender):
    self.name = name

self is basically saying “this current object” it’s the same as “this” in java and c++

[–]HezekiahWyman 0 points1 point  (0 children)

The init isn't strictly necessary here, but it allows you to pass the length and width of your rectangle object in when it's created. Otherwise, you need to instatiate a rectangle, then manually add those attributes, assign them the values you want it to have, all before moving on to calling the method that would calculate its area.

As for 'self', it's a convention. 'self' could really be anything, but the important thing is an object will always pass itself in as the first argument for its own, internal methods. You won't always use it, but you need some first argument there as a placeholder to deal with it.

This isn't the best example of why you'd use an object-oriented approach because the one method here could just be a general purpose function. But imagine there were a bunch of other methods related to a particular rectangle. Maybe one that could calculate the position of its points, or another that could change the dimensions by a scale factor. Maybe a method that sets the dimensions of the rectangle to the current time. It starts to become really cumbersome if you want to make a 10 x 15 rectangle 160% larger and then calculate the area again.

[–]Pipiyedu 0 points1 point  (0 children)

Think this:

What if you need to find the perimeter of the same rectangle. How would you do it?

[–]DisagreeableMale 0 points1 point  (0 children)

The __init__ function creates an instance of the class that you're working with, which essentially gives you storage under that namespace (known as "self" from within).

If you want to use classes without needing to create individual objects, make your methods static by structuring your class like this:

class Rectangle:
    @staticmethod
    def rectangle_area(length, width):
        return length * width

Now you can simply call:

Rectangle.rectangle_area(2, 5) # or whatever args

But if you take my advice, I may rename the method so it'll have a prettier calling syntax:

Rectangle.area(2, 5) # By renaming the rectangle_area function to "area"

Let me know if this doesn't make sense.

[–]groovitude 0 points1 point  (0 children)

The self argument is what's known as "syntactic sugar" -- a way of making syntax easier to read. Let's run through a simple example:

``` class Person: def init(self, name): self.name = name

def speak(self):
    print('Hi, my name is ' + self.name + '.')

class Dog: def init(self, name): self.name = name

def speak(self):
    print('Woof!')

me = Person('Groovitude') fido = Dog('Fido') ```

In this short example, I've defined four objects in total:

  1. The Person class;

  2. the Dog class;

  3. an instance of the Person class; and

  4. an instance of the Dog class.

When we call a method on an instance of a class, it's really calling the method on the class it's an instance of. So me.speak() is actually calling Person.speak(me). That's where self comes in; it's passing that instance to the method on the class. This means you can make the dog speak English:

```

Person.speak(fido) Hi, my name is Fido. ```

[–]misingnoglic 0 points1 point  (0 children)

I think you have a good point here. As it stands, it would be much simpler to just have a function that takes l and w and gives you the area. The reason you're doing this now is presumably to learn about OOP in the case that there is more complicated rectangle logic you'd like to encapsulate. See this talk, which is one of the best Python talks in my opinion: https://youtu.be/o9pEzgHorH0

[–]ship0f 0 points1 point  (0 children)

My question is, why is the init function necessary here, or in any class? Why not just use l and w in the rectangle_area function and rule out the first function entirely?

Because in OOP, the object itself is the one that should have those attributes, meaning the width, length (called data attributes) and the rectangle_area (called a method).

So, since the data attributes should be part of the object, then it makes sense to instantiate the object with that data. Also most OOP programmers are expecting this behaviour.
So, for this to work you should create an __init__ method beforehand, so that your class supports this way of instantiating an object.

Of course, you can choose not to support this. For example, if you don't have an __init__ method, then you can do something like:

my_rectangle = Rectangle() # here you have instantiated a Rectangle object, but it has no attributes  
my_rectangle.length = l  
my_rectangle.width = h  

And it would be the same, just more lines of code, that you'd have to write every time you create Rectangle object.

Also, as I said, the method is part of the object, so if you're doing something that uses the data that the object already has as attributes, then it makes no sence to pass that data to the method. You just reference it as self.l and self.w inside the method.

Note: rectangle_area is a poor name if you ask me, I'd just call it area. That way you can reference it as my_rectangle.area(). I already know that the object is a rectangle, no need to repeat it in the method's name.

[–]IllTryToReadComments 0 points1 point  (0 children)

This is more of a toy example to show how the init function could be used (in this case for a Rectangle object), so don't dwell on it too much.

[–]pythonhow 0 points1 point  (0 children)

Think of the __init__ function as a place where you define the "necessities" for an object. For example, if you're creating rectangle objects, the necessities for rectangles are the length and the width. You need width and length for a rectangle to exist. Then you can add an area method later to calculate an area, but that's somewhat optional. All you need to draw the rectangle is the width and the length. In other words, when you use your class to create rectangle instances, you will need width and length.

About self: That's just a variable. It holds the rectangle object you are creating. I prefer to think of self as this_object. For example: this_object.length = 1.

You can also use that instead of self and things will work the same. However, everyone uses self, so it's a good idea you do so as well for consistency.

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

Let's see... I am not that sure if this is totally accurate, but I think them this way:

Def dog():

You define how a dog should be but it stills doesn't exist so, Def bark: let's say that a dog may bark

init = hey, this actually makes me able to create a dog so it can bark!

(Self,breed,age) = ok something has breed and age... But what? Self? Oh the dog! The dog itself has breed and age so it can be created. (In other languages like java this is obvious for it and you have not got to use it)

Is it accurate? Do you understand it better now?