Trouble understanding classes in python

I am a beginner in python and I am having trouble understanding classes. I have a task that requires me to create a class that should return a students information e.g. name, id, age and marks. I have made a class but am having trouble with the output and keep on getting an attribute error:

print("Student: " + self.name + " ID: " + self.ID + " Age: " + self.age + " Mark: " + self.mark)
AttributeError: 'Student' object has no attribute 'age'

I was wondering if someone could explain what I am doing wrong here as I am quite lost.

Rest of the code:

import random

class Student:
    def __init__(self, name, ID):
        self.name = name
        self.ID = ID

    def setAge(self, age):
        self.age = age
        self.age = random.randint(0, 100)

    def setMarks(self, marks):
        self.marks = marks
        self.marks = random.randint(0, 100)

    def Display(self):
        print("Student: " + self.name + " ID: " + self.ID + " Age: " + self.age + " Mark: " + self.mark)

student = Student("John", "ID123")
student.Display()

You didn't call student.setMarks() and student.setAge() , so marks and age attributes are not created in object yet.

The solution is to call these two methods before calling student.Display()


In your example, you try to access the variable, before the assignment. You would actually have to call student.setAge and student.setMarks with arguments.

On the other note, in your function setAge you instantly overwrite the value, so consider removing either first or second assignment:

def setAge(self, age):
    self.age = age # first assignment
    self.age = random.randint(0, 100) # second assignment

Python objects are a container that has attributes you can set. If you don't set an attribute but try to read it, you get an AttributeError, meaning that the attribute you are looking for does not exist.

Currently, student = Student(...) calls Student.__init__, which assigns the name and ID attribute of the object. You never call student.setAge or student.setMarks, so your object's age and marks attributes are never set and can not be accessed.

It is traditional to assign default values in the __init__ method if you want to generally avoid unexpected crashes like that.

Another thing is that rather having getter and setter methods, as Java would, for example, Python encourages the use of properties. Properties are objects in the class template that can be accessed like a normal attribute, but allow you to run arbitrary code in place of the access and assignment operators.

Putting all that together, you could write something like

class Student:
    def __init__(self, name, ID, age=None, marks=None):
        self.name = name
        self.ID = ID
        self.age = random.randint(0, 100) if age is None else age
        self.marks = random.randint(0, 100) if marks is None else marks

    @property
    def marks(self):
        return self._marks

    @marks.setter
    def marks(self, value):
        # Example of a check you could do
        if not isinstance(value, int):
            raise TypeError('Marks must be an integer')