0

Is there a correct way to use the constructor arguments when I am creating other attributes? For example: do I use shape=arg3.shape OR shape=self.arg3.shape when I am creating self.arg4?

import numpy as np

class MyClass:

    def __init__(self, arg1, arg2, arg3=None, arg4=None):
        self.arg1 = arg1
        self.arg2 = arg2
        if arg3 is None:
            self.arg3 = np.array([[10, 10], [10, 10]])
        else:
            self.arg3 = arg3
        if (arg3 is None) and (arg4 is None):
            self.arg4 = np.ones(shape=(2,2))
        elif arg4 is None:
            print('Look at this case')
            self.arg4 = np.ones(shape=self.arg3.shape)
            # OR
            self.arg4 = np.ones(shape=arg3.shape)
        else:
            self.arg4 = arg4

    def print_args(self):
        print(self.arg1)
        print(self.arg2)
        print(self.arg3)
        print(self.arg4)

if __name__ == '__main__':
    x = np.array([[25, 20], [20, 25]])
    my_obj = MyClass(arg1=3, arg2=4, arg3=x)
    my_obj.print_args()

I'm guessing both work the same based on the output, but I am looking for an answer explaining what the best practice would be if any and the OOP reasoning behind it.

3 Answers 3

1

The two instructions shape=arg3.shape and shape=self.arg3.shape are different and they don't necessarily bring to the same output, according to the specific input given in the constructor. Let me explain why:

  • whenever you access a variable inside a method, if you prepend the keyword self, you refer to the object variable defined as a global variable within the object scope;
  • if you don't prepend the keyword self, you refer to any variable inside the function scope (e.g. inside __init__), that does not necessarily match the global variable inside the object.

That said, coming back to your question, and assuming you want to compute self.arg4 using the object variable self.arg3, then the correct instruction is shape=self.arg3.shape.

Why should you prepend self?

Shortly, self dereferences the memory where the object is stored, which is located in the process heap: here the object variables live.

The function, on the contrary, lives in the stack (as the function variables): it doesn't know where the object is and therefore where its variables are located in the heap. That's why you must use self.

Sign up to request clarification or add additional context in comments.

4 Comments

Your answer gave me a lot of insight. But in this case, since I am in the __init__() method does this really matter? Or is it just a good programming practice to use prepend self even inside __init__()? Since I am purely initialising the object variables based on arguments passed into the constructor (i.e. __init__())
It matters. To initialise an object variable you must prepend self: the __init__ method doesn't have anything special compared with other methods, and a variable without self inside __init__ is considered as a function variable, which will be "popped" from the stack together with the entire function stack as soon as __init__ returns.
I understand that to initialise arg4 I must use self.arg4 which I have done in both cases. However, my question is why should I use self.arg3.shape instead of arg3.shape to find the shape of arg3 in the line self.arg4 = np.ones(shape=self.arg3.shape). Because it's within __init__ it seems like either would be ok.
Well, if you refer to this specific __init__ implementation, with the exact logic you wrote, of course using either self.arg3 or arg3 makes no difference, simply because if arg3 is set, then self.arg3 is set equal to arg3. Thus, when you fall in the case where you set the print statement, both the variables point to the same obj.
0

Both shape=arg3.shape and shape=self.arg3.shape are not same.

In the first one, you using arg3.shape which points to the parameter from constructor. In the second one, you are using self.arg3.shape which points to the instance variable of the class. So, All instance variables should be accessed using self. You should not use self for variables in parameters.

3 Comments

Since it's at the initialisation stage does it really matter?
It is wrong to use self for variables in parameters. It will throw error. In your case it works because you have arg3 variable defined which is instance variable. So when you do self.arg3, it is using the arg3 variable which belongs to class. When you do just arg3, it will use the value from constructor parameter.
Use different name for variables in constructor and instance variables. You will understand it better.
0

You should do this

self.arg4 = np.ones(shape=self.arg3.shape)

This is purely to increase the readability of the code.

self.arg4 = np.ones(shape=arg3.shape)

You are free to do anything that you like but this is just my opinion.

2 Comments

On Python 3.8.3 it runs without any error. Do you know why "any variable that belongs to that instance should be accessed by putting self. prior to it"?
It's the way classes work in python. But here, you have a parameter named arg3 and an instance variable named arg3, so anything will work. But I personally find it easier to understand if I am using instance variables with the parameter value. That is just a readability purpose.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.