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.

8 Likes

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

3 Likes

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.

2 Likes

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__().

8 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))

1 Like

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:

2 Likes
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?

Nope, copy/pasted your code into the exercise & it doesn’t say there’s any mistake.

As per my understanding you can use either self or the class name, no probs.

this explanation in very good i think it should be part of lessons

why this code raise NameError:

class Store:
  exmp = 5
  def example():
    print(exmp)
    
Store.exmp()

and in functions inner functions have access to outer functions variables . given exmp of your self :

from collections import namedtuple


def Counter():
    begin = 0

    def increment():
        nonlocal begin
        begin += 1

    def read():
        return begin

    # namedtuple provides attributes so that seems like an appropriate
    # representation, could be any container, including a function
    return namedtuple("Counter", ["increment", "read"])(increment, read)


c = Counter()
print(c.read())  # 0
c.increment()
c.increment()
c.increment()
print(c.read())  # 3

You are attempting to make a call on an integer. Store.exmp in your code is equal to 5. This should be a TypeError, rather than a NameError. A shorter example of this would be as follows-
test_val = 3()

I’m not sure what issue you have with the second section of code. Could you provide more details about what problem you are having with it?

1 Like

when I delete this line :

return namedtuple("Counter", ["increment", "read"])(increment, read)

I get and error :

Traceback (most recent call last):
  File "script.py", line 16, in <module>
    print(c.read())  # 0
AttributeError: 'NoneType' object has no attribute 'read'

why this won’t work :

def Counter():
    begin = 0

    def increment():
        nonlocal begin
        begin += 1

    def read():
        return begin


c = Counter()
print(c.read())  # 0

Did you check the error itself?
What type is c in the altered code?

Traceback (most recent call last):
  File "script.py", line 13, in <module>
    print(c.read())  # 0
AttributeError: 'NoneType' object has no attribute 'read'

It wont print type © Only print this error

I’m not certain what you’re trying to do at this point, I assume you were just testing bits of code to see what happened so I phrased it as a question in the hope that you’d play around with it a bit. Your error occurs as your function has no return and therefore calling it will return the default, None.
There are very few things you can do with a None object, with good reason.
Are you sure you want to be using nested functions in the first place?

Hopefully that explains why your Error appears. If you have a specific issue with further code it would be best to send it to the python get-help section of the forums-
https://discuss.codecademy.com/c/get-help/python/