Not sure what "not callable" means in this case

class student:
    stuCount=0

    def __init__(self, fname, lname, subject, grade):
        self.fname=fname
        self.lname=lname
        self.subject=subject
        self.grade=grade
        student.stuCount+=1

    def displayStudentsCount(self):
        print("Total amount of students is %d "%(student.stuCount))

    def studentInformation(self):
        print("First Name: "+self.fname+ '\n'+"Last Name: "+self.lname+ '\n')

print("Press Q to quit \nAdd to addstudent\nDisplay to display info\n Student Count for enrolled students")

while True:
    option=input('Select Option')
    if option=='Q':
        break
    elif option=="Add":
        first_name=input('Enter first name: ')
        last_name=input ('Enter last name: ')
        subject=input ('Enter subject: ')
        mark=input ('Enter grade: ')
        student= student(first_name, last_name, subject, mark)

    elif option=="Display":
        student.studentInformation()

    elif option=="Student Count":
        student.displayStudentsCount()

I can enter the first student info no problem and the other option work as well but when I enter the last detail (“Enter Grade”) on a second student I get an error:

Traceback (most recent call last):
File “C:/Users/doomc/AppData/Local/Programs/Python/Python311/createfile.py”, line 28, in
student= student(first_name, last_name, subject, mark)
TypeError: ‘student’ object is not callable

What am I missing?

Thanks!

You have a name clash: you defined a class called student and then you made a separate variable called student that is an instance of that class.

After the first pass through your while loop, the class is no longer available, you just have a single instance. When you try to create a new instance, you’re writing [instance of student].__call__(arguments...) and you get the TypeError because the instance is not callable.

An easy fix is to use different names for the different things. Name your class Student with a capital S (this is the convention for classes in Python), and you’ll have student = Student(...)

1 Like

Hi James, thanks so much for the reply! I made some changes but still getting an error…


class Student:#changed to Student
    stuCount=0

    def __init__(self, fname, lname, subject, grade):
        self.fname=fname
        self.lname=lname
        self.subject=subject
        self.grade=grade
        student.stuCount+=1

    def displayStudentsCount(self):
        print("Total amount of students is %d "%(student.stuCount))

    def studentInformation(self):
        print("First Name: "+self.fname+ '\n'+"Last Name: "+self.lname+ '\n')

print("Press Q to quit \nAdd to addstudent\nDisplay to display info\n Student Count for enrolled students")

while True:
    option=input('Select Option')
    if option=='Q':
        break
    elif option=="Add":
        first_name=input('Enter first name: ')
        last_name=input ('Enter last name: ')
        subject=input ('Enter subject: ')
        mark=input ('Enter grade: ')
        student= Student(first_name, last_name, subject, mark)#changed to Student

    elif option=="Display":
        Student.studentInformation()#changed to Student

    elif option=="Student Count":
        Student.displayStudentsCount()#changed to Student
        

Traceback (most recent call last):
File “C:/Users/doomc/AppData/Local/Programs/Python/Python311/createfile.py”, line 28, in
student= Student(first_name, last_name, subject, mark)#changed to Student
File “C:/Users/doomc/AppData/Local/Programs/Python/Python311/createfile.py”, line 9, in init
student.stuCount+=1
NameError: name ‘student’ is not defined. Did you mean: ‘Student’?

Hey James, I meant to reply to your post directly… please see it on the main post comment and thanks again for your help!

Sorry for all the messages- I figured out that I needed to have the class uppercase S in Student.stuCount but now I get this:

Traceback (most recent call last):
File “C:/Users/doomc/AppData/Local/Programs/Python/Python311/createfile.py”, line 34, in
Student.displayStudentsCount()#changed to Student
TypeError: Student.displayStudentsCount() missing 1 required positional argument: ‘self’

Look at the definition of displayStudentsCount. It expects an argument, namely, a student instance (self). You’re not passing one, not surprising given that it’s meant to be a class method, i.e. a method related to the class itself, not an instance of that class.

Try this:

    @classmethod
    def displayStudentsCount(cls):
        print("Total amount of students is %d "%(cls.stuCount))

Now that you’ve fixed the original issue by using separate names for the class and its instances, and making sure to use the right name in each context, you might find this talk by @rhettinger useful:

The talk goes into a fair amount of detail about all the relevant concepts, and works through examples at a natural speaking pace. You might prefer that kind of explanation. However, there isn’t a clear specific point to jump in and say “this is where the answer is for your question”. The talk uses its own examples, with their own motivations, and each idea is designed to build upon the last.

So in case you don’t have 45 minutes to spare, I’ll try to answer directly - but it’s still a bit long because I have to explain some concepts.

In your case, the displayStudentsCount code is supposed to relate to the students taken collectively, rather than to an individual student. Ordinary methods that you write in a class are supposed to be called on individual instances of the class. (Remember: the primary purpose of a class is to define a data type; the instances that you create are just objects of that type. The relationship between the Student class, and each student that you create inside the loop, is the same as the relationship between the built-in int type and individual integers like 1 that you use in the program.)

In Python, we can use a classmethod to add code to the class that is “about” the class, which doesn’t require an instance, but is specific to the class (and can be specialized for subclasses). We simply add the @classmethod *decorator, like so:

class Student:
    stuCount=0
    # other methods as before

    @classmethod
    def displayStudentsCount(cls):
        print("Total amount of students is %d "%(cls.stuCount))

Notice that I accepted a parameter named cls, and use it in the logic. The name cls does not have any special meaning to Python - it’s just a convention, exactly like how self is for ordinary methods. But because we used the decorator, inside the method, cls will be a name for the class, instead of an instance.

We can call it either from an instance, like student.displayStudentsCount(), or directly from the class, like Student.displayStudentsCount(). Either way, the method is properly found, and cls gets the right value.

Inside the classmethod, we can directly look up the stuCount value in the Student class (Student.stuCount). But by using the passed-in cls, we get to re-use the logic for subclasses, and each subclass can potentially have its own stuCount which can be used when we call displayStudentsCount for the subclasses.

Another option is to use staticmethod instead of classmethod. It looks like:

class Student:
    stuCount=0
    # other methods as before

    @staticmethod
    def displayStudentsCount():
        print("Total amount of students is %d "%(Student.stuCount))

Unlike in other programming languages, such a “static method” can still be called using an instance. However, the method will not be passed either an instance or a class object automatically; therefore, the logic must directly use Student to look up the value that it’s displaying. This also means that subclasses will use the same stuCount value, unless they explicitly override the displayStudentsCount code to do something else (that is, they don’t benefit from polymorphism).


Here’s some more detail about the original issue.

“Not callable” means exactly what it sounds like - the code tries to call student, by using the function-call syntax. In Python, many things can be “called” that are not functions. In particular, in the Python worldview, when we use a class to create an instance, we are “calling the class”.

But of course, I can see this isn’t really you being “not sure” about the “meaning” of the words - it’s about understanding why the error occurred, right? As you’ve already worked through, it was because a name can only refer to one thing at any given time in the program. Originally, the class student: block made the name student refer to that class. But running student= student(first_name, last_name, subject, mark), the first time, means that an instance of the class is created (by calling the class), and then student refers to that instance. If it refers to the instance, then it can’t refer to the class any more. So, the next time through the loop, the same code will fail - it tries to do student(first_name, last_name, subject, mark) again, but that now means to call the instance that was created the first time - which isn’t possible, exactly as the error message says.

The general form of this problem is called shadowing. it’s the same thing that happens, for example, if you try to use the builtin name str as a name for some arbitrary string, and then try to convert a value to string using str later. There are a few popular Q&As for this on Stack Overflow, and we haven’t managed to choose the “best” one although they’re clearly all the same thing:

And a related Q&A that just explains the concept without focusing on fixing an error:

1 Like

Sorry about the delay in replying- I’ve been having a hard time finding time. At work I’ve tried to watch a whole bunch of videos trying to explain these concepts and slowly they are making sense. I find the logic aspect of programming to be a bit confusing but I’m sure over time it will just make sense. I added the classmethod to the student count and it worked. Now I’m just trying to sort out the Display method and im getting more errors. I tried incorporating both classmethod and staticmethod (but I know it requires the fname and lname variables so staticmethod wont work) but I keep getting this error:

Traceback (most recent call last):
File “C:/Users/doomc/AppData/Local/Programs/Python/Python311/student.py”, line 31, in
Student.studentInformation()#changed to Student
TypeError: Student.studentInformation() missing 1 required positional argument: ‘self’

I don’t understand because it says its missing self but it is there:

 def studentInformation(self):
        print("First Name: "+self.fname+ '\n'+"Last Name: "+self.lname+ '\n')

Here is the whole code so far:

class Student:#changed to Student
    stuCount=0

    def __init__(self, fname, lname, subject, grade):
        self.fname=fname
        self.lname=lname
        self.subject=subject
        self.grade=grade
        Student.stuCount+=1

    @classmethod
    def displayStudentsCount(cls):
        print("Total amount of students is %d "%(cls.stuCount))
    
    def studentInformation(self):
        print("First Name: "+self.fname+ '\n'+"Last Name: "+self.lname+ '\n')

print("Press Q to quit \nAdd to addstudent\nDisplay to display info\n Student Count for enrolled students")

while True:
    option=input('Select Option')
    if option=='Q':
        break
    elif option=="Add":
        first_name=input('Enter first name: ')
        last_name=input ('Enter last name: ')
        subject=input ('Enter subject: ')
        mark=input ('Enter grade: ')
        stuDent= Student(first_name, last_name, subject, mark)#changed to Student

    elif option=="Display":
        Student.studentInformation()#changed to Student

    elif option=="Student Count":
        Student.displayStudentsCount()#changed to Student

I really appreciate your help.

Thanks,
Dave

Note the spelling here:

stuDent= Student(first_name, last_name, subject, mark)#changed to Student

You never refer to stuDent anywhere else. I think you meant student.

Here:

Student.studentInformation()#changed to Student

you have Student, which is the name of the class, but you’re calling studentInformation, which is an instance method. This should be student.

Student.displayStudentsCount()#changed to Student

This is OK because displayStudentsCount is a class method and Student is the class.

1 Like

As we covered already, this is an ordinary method, not a class method. It’s something that we use with an instance of the class. It tells us something about a specific student, not about students in general. The self parameter will be filled with the student whose information is displayed.

Student means the class, not any particular student, so this uses the method in the wrong way. There is no self value being provided. In order to display a student’s information, we need to know which student to use.

1 Like

Thanks so much!

Thanks so much and for your patience. Finally this makes sense.