Class variables scope

In exercise https://www.codecademy.com/courses/learn-python-3/lessons/data-types/exercises/self I am running into a dilemma related to the scope of a Class variable and the Class methods.

When I run:

class Circle:
  pi = 3.14
  def __init__(self, diameter):
    print("Creating circle with diameter {d}".format(d=diameter))
    # Add assignment for self.radius here:
    self.radius = diameter/2
    
  def circumference(self):
    return 2 * pi * self.radius

I get a: NameError: name ‘pi’ is not defined
When I use instead the code

class Circle:
  pi = 3.14
  def __init__(self, diameter):
    print("Creating circle with diameter {d}".format(d=diameter))
    # Add assignment for self.radius here:
    self.radius = diameter/2
    
  def circumference(self):
    return 2 * self.pi * self.radius

everything is working fine.

I thought that pi, being defined outside the method, would be accessible directly. Why do I get a NameError when I use pi in the method definition without the “self”

If you call it as simply pi, the interpreter thinks that you are referring to a local variable defined within the namespace of your method. Since the variable has not been defined, you get the NameError.

Since pi is in the Circle namespace, and you (presumably) do not plan to change it, it is best (from a style or readability standpoint) to call it inside of the circumference() method as Circle.pi, not self.pi.

If you were using the class attribute as, for instance, a default value that can vary with the instance, then my_var = self.my_var would be appropriate.

it is best (from a style or readability standpoint) to call it inside of the circumference() method as Circle.pi , not self.pi .

If you were using the class attribute as, for instance, a default value that can vary with the instance, then my_var = self.my_var would be appropriate.

Hi patrickd314,
thank you. A light bulb came over my head after reading your answer. Now I understand :smiley:

One way to keep track of this is to think of when one instance decides the change the value of a class variable. That value will never change unless done directly… Class.variable = %% which will change the value globally for all instances from that moment forward.

If an instance changes the value, then it becomes the owner of the new value, which is now an instance variable.

self.variable = %%

will apply only to that instance as if it were an intialized variable. None of the other instances will see the change and will still be able to draw upon the class variable.

The real difference here is in the accessing. All instances can access the class variable as though it was an instance variable, using instance.variable notation, and there is no harm in polling it this way. Changing, as amentioned above means taking ownership of the new value by the instance that changed it. Now that instance can only access the class variable by its formal name… Class.variable.