FAQ: Learn Python: Classes - Constructors

This community-built FAQ covers the “Constructors” exercise from the lesson “Learn Python: Classes”.

Paths and Courses
This exercise can be found in the following Codecademy content:

Computer Science

FAQs on the exercise Constructors

Join the Discussion. Help a fellow learner on their journey.

Ask or answer a question about this exercise by clicking reply (reply) below!

Agree with a comment or answer? Like (like) to up-vote the contribution!

Need broader help or resources? Head here.

Looking for motivation to keep learning? Join our wider discussions.

Learn more about how to use this guide.

Found a bug? Report it!

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

2 posts were split to a new topic: What does instantiate mean in the context of this lesson?

I noticed a difference with my solution and official, it happens a lot of course but this time something bugged me.

My way is this:

class Circle:
  pi = 3.14
  
  # Add constructor here:
  def __init__(self, diameter):
    print("New circle with diameter: {}".format(diameter))
teaching_table = Circle(36)

This one is official:

class Circle:
  pi = 3.14
  
  # Add constructor here:
  def __init__(self, diameter):
    print("New circle with diameter: {diameter}".format(diameter=diameter))
    
teaching_table = Circle(36)

Official solution’s syntax used to format seems like a little bit more work than what I did. In which situations the mothod used in a solution would be preferable way to handle .format()?

It appears to me a function can do the same thing with less lines in this exercise. What’s the unique strength of using class and a init method? Thanks.

Being a blueprint for a custom object, we can instantiate multiple objects of the class and give them all the same set of custom methods and apply them to each object’s unique attributes.

1 Like

Can someone explain the .format use in this?

1 Like

https://pyformat.info/

Give that page a read and your question should be answered.

If possible , please simplify ( with an example or something ) , I didn’t understand . I also think function were better and less confusing .

Functions have there use cases, classes have theirs. One is able to act upon random objects (of suitable type) while the other is able to act only upon instances of the class (custom objects).

>>> def sss_triangle_area(*sides):
    '''
    Area of S-S-S triangle using Heron's Formula
    '''
    if len([*sides]) != 3: raise ValueError
    a, b, c = sides
    try:
        (a + b + c) / 0
    except ZeroDivisionError:
        pass
    if a + b <= c or b + c <= a or a + c <= b: raise ValueError
    s = (a + b + c) / 2
    return (s * (s - a) * (s - b) * (s - c)) ** 0.5

>>> sss_triangle_area(3,4,5)
6.0
>>> sss_triangle_area(1,4,5)
Traceback (most recent call last):
  File "<pyshell#228>", line 1, in <module>
    sss_triangle_area(1,4,5)
  File "<pyshell#226>", line 11, in sss_triangle_area
    if a + b <= c or b + c <= a or a + c <= b: raise ValueError
ValueError
>>> sss_triangle_area('3','4','5')
Traceback (most recent call last):
  File "<pyshell#229>", line 1, in <module>
    sss_triangle_area('3','4','5')
  File "<pyshell#226>", line 8, in sss_triangle_area
    (a + b + c) / 0
TypeError: unsupported operand type(s) for /: 'str' and 'int'
>>> 

Above we’ve written a function that resides in the global namespace. It contains all the validation it needs to ensure the sides are values and that they indeed represent a triangle.

That’s just one method for computing area. There are SAS, ASA, and AAA methods as well. That means having four functions in the namespace that have no real connection to each other. By writing a class definition we can include all the methods within the one definition and have them all apply to valid object instances.

With a class we have only to define an instance once, and after that all methods are accessible without having to give arguments every time like we would have to do with the four functions mentioned above.

Think on this for a time, while I drum up an example of a class to create triangle instances.

Got stuck with these bunch of questions –

  1. Why don’t we instantiate the class at first when we are using the __init__ constructor?

  2. Can’t we call the __init__ like we do in case of calling methods in a class?

  3. Are the constructors all built-in? __init__, __repr__, __add__, __iter__ etc. Can we make constructors of our own?

  4. I don’t see any use of self in the constructor. Why are we still using it? In this exercise about calling methods it says, self refers to the object. I understand, but don’t know why I am still confused about this.

  5. If there are more than one constructors in a class, shall we still call the object like shout1 = Shouter("shout") as we did in here
    image
    or there are other ways to call the object then?

I’m sorry if the questions are stupid. I’m just too stuck with these.
Thanks for your answer!

1 Like

Your implementation does not necessarily need a constructor method (__init__()). Let’s examine that…

class Shouter:
    def shout(phrase):
        print (phrase.upper() if type(phrase) == str else 'Error!')
>>> Shouter.shout('OUCH!')
OUCH!
>>>

There is only one constructor in a class, __init__(). The others mentioned are built-in dunder methods. We can write all manner of methods on our class. I’m not sure we can write our own dunder methods, though.

self is the built in object that identifies our instance to any methods we may call on it.

Above we called the class itself and invoked its shout() method. We could have created an instance, first, then called the method on the instance…

>>> ouch = Shouter()
>>> ouch.shout('ouch!')

TypeError: shout() takes 1 positional argument but 2 were given

That means we need to add another parameter. Can you guess what that might be?

class Shouter:
    def shout(self, phrase):
        print (phrase.upper() if type(phrase) == str else 'Error!')

Now let’s create an instance…

>>> ouch = Shouter()
>>> ouch.shout('ouch!')
OUCH!
>>>

Note where the argument is written… In the call. We can initialize the instance to have that as a built in phrase.

>>> class Shouter:
    def __init__(self, phrase):
        self.phrase = phrase
    def shout(self):
        print (self.phrase.upper() if type(self.phrase) == str else 'Error!')

        
>>> ouch = Shouter('ouch!')
>>> ouch.shout()
OUCH!
>>> 

Note the difference from the earlier version.

1 Like

Thanks a lot!
I was confused about this that why do we have to assign phrase to self.phrase

but got to understand it from the later exercises. It’s for preserving the value of the function parameter, assigning it in instance variable is a simple way to do so. And also it’s name can be different.

1 Like

Why I am getting type error ?

class Circle:
  pi = 3.14
  
  # Add constructor here:
  def __init__(self, diameter):
    return "New circle with diameter: " + str(diameter)


teaching_table = Circle(36)

Why return is not working here but print works

The __init__() method does not return anything, it assigns the parameters to instance attributes.

>>> class Circle:
    def __init__(self, diameter):
        self.diameter = diameter
    def __str__(self):
        return f"Circle with diameter: {self.diameter}"

>>> teaching_table = Circle(36)
>>> print (teaching_table)
Circle with diameter: 36
>>> 

why do we have to say diameter=diameter ? Is it because we technically have 2 variables( ie. self, diameter) but only one {}

  def __init__(self, diameter):
    print("New circle with diameter: {diameter}".format(**diameter=diameter))`**``

The real variable is assigned to the placeholder variable so that it can be named in the template. We could have written,

print("New circle with diameter: {d}".format(d=diameter))

It’s really just a more formal way of writing,

 print("New circle with diameter: {}".format(diameter))

The new, f-string deals with the formality in a direct way…

 print(f"New circle with diameter: {diameter}")

All formatting methods are still valid, so pick the one you prefer to use and stick with it for consistency.

1 Like

As I remember correctly, the format as shown by the solution comes in handy when you use multiple variables in the format which aren’t used in the given order.

etc. "text {var1} text {var3} text {var2}.format( var1 = "first text, var2 = “second text”, var3 = “third text”)

This really threw me also. If the argument for using this is about order, surely it’s still easier to list them in the order that you want at the end within .format without using the additional code?

Hi, I’m new to coding and I don’t really understand the .format() function. What is it’s purpose? I tried to use the following code but it doesn’t call the input to my Circle class:

class Circle:
pi = 3.14

Add constructor here:

def init(self, diameter):
print(“New circle with diameter: {diameter}”)

circle = Circle(36)

Why do I need to use .format()?

Because that is the method that supplies the value for diameter so it can be interpolated into the string.

print ("New circle with diameter: {diameter}".format(diameter=self.diameter))

New circle with diameter: 36

To enclose the variable inside the braces we need to declare it as a keyword. Also note that it must be written as an instance variable.

https://pyformat.info/