What are the differences between instance variables and class variables, or why do I need to use `self.pi`?

This section was really difficult! I think it’s gonna take some time for me to really understand OOP. Can anyone point me to any good books or somewhere to practice this?

I have a question about this assignment. I’m really struggling with it.

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

I get that self.pi refers to the variable pi in the Class. Why is there a self.radius, but there is no variable for it? It looks like the method circumference(self) is calling it even though it was defined in init (self, diameter). Is it because init is a special constructor?

In Python, it is an instance attribute constructor only, since the object exists the moment it is instantiated. Try removing the method and see if it prevents us from creating a new instance.

I can save you the trouble and tell you, no; it won’t have any bearing. A new instance can be created. It just won’t have any instance variables, only the methods and class variables it inherited.

One thing to note is that there is no record of diameter apart from its transient role in the instance intialization. Once that method has run its course, it no longer exists.

The variable that is preserved is radius, and every instance has the value set for this that was computed from the diameter when the instance was created. Because we are working with a singular code base serving multiple instances, we need to know which instance is accessing this code.

instance = Circle(10)

The instance can access the circumference method with no arguments, since self creates a binding to it and is able to access its radius attribute, 5.

instance.circumference()    # 31.4
1 Like

I wonder why it is written this way:

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 self.radius * Circle.pi * 2

Instead of this way?:

class Circle:
  pi = 3.14
  radius = 0  #<----------------
  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 self.radius * Circle.pi * 2

I think maybe @aorchowski was asking the same thing. It seems to me that there is a variable scope issue in the former. Right?

Or will there be an inheritance issue with placing “radius = 0” as a Class level attribute?

Will it create two attributes, one for self and one for the “Circle” class?

There won’t be a scope related issue and setting radius as a class variable has no effect since the instance variable overrides that.

No, just the one will exist, the instance variable. Instances all inherit the same class variables but any one instance can assign a new value to the variable since it is accessed the same way.

instance.radius = new_radius

Now that instance has its own unique value for the radius, making it essentially an instance variable.

I see. That makes sense.

What if “Circle.radius” is used? Will it now be a class level attribute and be inherited by instances ?

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

It seems to me that constants pi and radius (when expressed as half the diameter) should be class level attributes

That would be non-standard. It should be an instance variable with self.

Interesting. In this video the instructor talks about both instance variables and class variables and why you would use them. Maybe I’m confusing the use case.

how can i access the argument passesd at the time of instance in another method?

here what i want to say:
class test:
def init(self,name):
self.shame = name
def func(self):
return name

if i run
foo = test(“trial_name”)
print(foo.func())

i should get “trial_name” printed

class test:
  def init(self,name):
    self.name = name
  def func(self):
    return self.name

foo = test('trial_name')
print (foo.func())    # trial_name

Can you explain this again?
How are we able to get self.radius?
It is because we have diameter as an argument in method?
Or we just can create any instance variable with the value we define.

radius would be a computed value if the parameter is diameter. I would write a separate method since we want the radius to reflect the currrent diameter, and we may want our class to allow that to be changeable.

def  get_radius(self):
    return self.diameter / 2

I’ll need to be apprised of the exercise, so please post a link and we can look at this a little further.

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

Ah, so the instructions stipulate writing the radius as a part of the initialization. This will be okay if the circle instance is to be static, and unchangeable.

Note that the instance has no diameter attribute, only a radius computed from the parameter.

1 Like

I am having trouble understanding why in:

def circumference(self):
return 2 * self.pi * self.radius

We use self.pi instead of just pi. I understand why we use self.radius because we have defined it above but not pi.

pi is a class variable:

class Circle:
  pi = 3.14 # class variable

class variables are automatically added to self.

if you intent to use pi, how is that defined within the scope of circumference method?

Two questions if I may:

  1. Why do we write self.radius and self.pi the same when we are creating the circumference method? pi is a class parameter and self.radius is an instance variable, no?

  2. Why can’t def circumference(self): be at the same indentation as the self.radius code?

TIA!

to access class variables, instance variables and methods within a method you need self.

class variable and methods are automatically added to self

Why would you want to do that? You want to add the method to the class, not the constructor

1 Like

It simply depends upon how you want to construct the class, and what you want each object to do. In real life, i.e., when not following a lesson plan, you can do it almost any way that you want.

In this particular design,
self.radius sets an attribute of the particular Circle object.
circumference() is a method of the Circle object.

Since the circumference (and area) of a given circle are fixed, self.circumference and self.area could indeed be included in the constructor to establish those values as object attributes. However, presumably in order to easily illustrate attributes and methods, the course author chose not to do it that way.

3 Likes

I’m having some issues with classes but as i go over it more it gets more understandable. I think just a bit more practice than other topics is fine.

its not as complex as you think but at the start it is a bit dodgy

To reiterate, there is a case for using self on all variables, whether class or instance.

Consider,

>>> class Circle:
  pi = 3.14
  radius = 0
  def __init__(self, diameter):
    print("Creating circle with diameter {d}".format(d=diameter))
    Circle.radius = diameter / 2

  def circumference(self):
    return Circle.radius * Circle.pi * 2

>>> this = Circle(10)
Creating circle with diameter 10
>>> this.circumference()
31.400000000000002
>>> that = Circle(100)
Creating circle with diameter 100
>>> that.circumference()
314.0
>>> this.circumference()
314.0
>>> this.radius
50.0
>>> 

Notice that this is no longer what it was initialized as? That is because the class variable radius has been overwritten with the new computed that value.

Now let’s see it when we use self

>>> class Circle:
  pi = 3.14
  def __init__(self, diameter):
    print("Creating circle with diameter {d}".format(d=diameter))
    self.radius = diameter / 2

  def circumference(self):
    return self.radius * self.pi * 2

>>> this = Circle(10)
Creating circle with diameter 10
>>> that = Circle(100)
Creating circle with diameter 100
>>> this.circumference()
31.400000000000002
>>> that.circumference()
314.0
>>> this.radius
5.0
>>> that.radius
50.0
>>> 

Each instance is unique to the point that even the value for pi can be set in each.