What is the difference between calling a variable with `self` or the `ClassName`?

Hello, @mtf ! Sorry, if my question is stupid or if I’m missing something, I just wanna make sure I got it correctly. For the next example/exercise, we have this example [ Medthods with Arguments > https://www.codecademy.com/paths/computer-science/tracks/cspath-python-objects/modules/cspath-python-classes/lessons/data-types/exercises/methods-with-arguments]:

class DistanceConverter:
  kms_in_a_mile = 1.609
  def how_many_kms(self, miles):
    return miles * self.kms_in_a_mile

converter = DistanceConverter()
kms_in_5_miles = converter.how_many_kms(5)
print(kms_in_5_miles)
# prints "8.045"

image

1.) Should we not have return self.miles * self.kms_in_a_mile?

1.1 - self.miles and DistanceConverter.miles are the same thing (since kms_in_a_mile is actually an attribute/class variable of the class DistanceConverter)?

2.) self.example is needed whenever we call elements outside the function/method but inside the class?
3.) Also, why is class DistanceConverter not having ()? (is this a special case/scenario…?)

Thank you a lot!

The miles parameter is not an instance variable, so no, it will not be written as self.miles. It is direct input to the method and not instantiated with the class.

In truth, neither of them exist. What does exist is the class method and class variable so this class does not need to instantiated.

kms_in_5_miles = DistanceConverter.how_many_kms(5)
  1. self identifies the context object whether it is an instance or a direct call, as above.

  2. When there is no inheritence from a super class, the parens are not required in Python 3.

3 Likes

Thank you a lot! @mtf , very explicit, extremely helpful :pray::pray:

1 Like

In the example given , the class variable is called using self.variablename. But in the exercise while doing the problems when i did the same way it didnot accept it and said it has to be classname.variablename please explain.

1 Like

It depends upon what you want to do with your class variable. If it is just to be a constant like pi, you can access it either way, although I prefer using the Class_name.var style, since it is defined in the class namespace.

If you want it to be mutable, but evenly treated by every instance of the class, then also use Class_name.var. A good example of this is augmenting the variable by 1 every time there is a new instance, to keep track of how many instances are out there.

But, once you call the variable using self.var, that variable becomes an instance variable. Its name becomes locked into the particular instance that called the method. It is no longer a class variable as far as that instance is concerned

class Test_var:
    class_var = 10
    def method_1(self):
        Test_var.class_var += 5
    def method_2(self):
        self.class_var += 5
# Now instantiate two instances        
test_1 = Test_var()    # This one will only use method_1
test_2 = Test_var()    # This one will only use method_2

test_1.method_1()    # augments  Test_var.class_var by 5
print("Test_var.class_var = {}\ntest_1.class_var = {}  \ntest_2.class_var = {}  \n*****". format(Test_var.class_var, test_1.class_var, test_2.class_var))
test_2.method_2()    # augments  self.class_var by 5
    
print("Test_var.class_var = {}\ntest_1.class_var = {}  \ntest_2.class_var = {}  \n". format(Test_var.class_var, test_1.class_var, test_2.class_var))

Output:

Test_var.class_var = 15
test_1.class_var = 15  
test_2.class_var = 15  
*****
Test_var.class_var = 15
test_1.class_var = 15  
test_2.class_var = 20  

So instance test_1 augmented the class variable to 15, and then test_2 hijacked the variable name class_var and incremented it an additional 5, without any effect on the actual class variable.

If you wanted the class variable, for example, to be an initial value of something for every instance, something that would change as time progressed (like strength or health in a game character), this technique of using self with a class variable would work, but it would (IMHO) be more Pythonic to do that with __init__().

2 Likes

self.pi vs class.pi

My code returned an error even though the output seemed to be correct. I suspect it has something to do with the fact that I used self.pi instead of Circle.pi.

Can someone confirm this? The error was that the output for pizza_area did not equal 113.04.

Here is my code:

class Circle:
  pi = 3.14
  def area(self, radius):
    area = self.pi * radius **2 
    return area

circle  = Circle()


pizza_area = 12

teaching_table_area = 36

round_room_area = 11.460 

print(circle.area(pizza_area /2))

print(circle.area(teaching_table_area / 2))

print(circle.area(round_room_area / 2))

It will accept self.pi or Circle.pi (which I prefer). Your problem is this part of the instruction:
.

You go to measure several circles you happen to find around.

  • A medium pizza that is 12 inches across.
  • Your teaching table which is 36 inches across.
  • The Round Room auditorium, which is 11,460 inches across.

Compare with the values you have assigned.

1 Like

Thank you @patrickd314 for the explanation of this particular issue. As a beginner I did not know any of that and it took me about 3 attempts to understand it so loads of kudos to you! :green_heart:

1 Like
class Circle():
  pi=3.14
  
  def area(self,radius):
    return Circle.pi*radius**2

my logical say it soppuse to be like:

class Circle():
  pi=3.14
  
  def area(self,radius):
    return self.pi*radius**2

and the website say its mistake…can somone explain why?