-1

I am trying to understand Python 3's syntax for class definitions. I saw that @classmethods seem to be typically used to distinguish a class method and static methods work as methods solely dependent on the parameters passed to it. I don't think I understand the actual application of @classmethod and the implication that "Class method works with the class since its parameter is always the class itself."

Take this example below:

class Student():
    # Private attributes of Student Class
    __student_id = None
    __student_name = None


    # Constructor (__init__) 
    def __init__(self, student_id: int, student_name: str):
        self.__student_id = student_id
        self.__student_name = student_name 


    # Public Access Methods
    @classmethod
    def assign_lab_day(self, day: int) -> None:
        self.__lab_day = day


    # Print student details
    #@classmethod
    def display(self) -> None:
        print(f'Student ID: {self.__student_id}')
        print(f'Student Name: {self.__student_name}')


...


student1 = Student(12345678, 'Jane')
student1.display()

Result

Student ID: None
Student Name: None

Here everything seems to be okay however added the @classmethod to the display function causes the results to be the initialization of None. Given that a display function has the parameter of it's self i.e the host class instance, why would this occur?

Ref

CXX_NVCC
  • 31
  • 4
  • _however added the @classmethod to the display function causes the results to be the initialization of None_ I have no idea what that means. Please show the actual code you tried, and the actual results, and explain what results you expected instead. – John Gordon Jul 24 '23 at 01:32
  • Also, the use of `self` in the argument list of `assign_lab_day()` is quite misleading. Of course you are free to use whatever argument names you like, but the first argument passed to a classmethod is the **class**, not the **instance**. Using `self` to mean something other than the instance of the class is very much against convention. – John Gordon Jul 24 '23 at 01:35
  • I have added the line of the class method the code provided is the same, it's a pretty simple test example I am playing with – CXX_NVCC Jul 24 '23 at 01:37
  • The `Student` class has no such method `print_student_details()`. This is obviously not your real code. – John Gordon Jul 24 '23 at 01:41
  • Adjusted, It's not different code, the original questions stems from an example piece with different function names and more random attributes. That old example if not of my concern, it is the same code bar that typo. The question is not related to the original example as I was trying to introduce myself to the conept which is new to me. If you copy the code existing above into a python file the exact problem arises by removing the comment on the classmethod. If this is not enough the post should be closed as I cannot give anymore that whats specified above. – CXX_NVCC Jul 24 '23 at 01:51
  • 1
    If you uncomment the `@classmethod` decorator, then `self` isn't really `self` anymore. The automatic argument that is passed to class methods is the **class**, not the **instance**. As I commented above, you are free to call it `self` if you like, but that's very a misleading name in this case. – John Gordon Jul 24 '23 at 01:59
  • I was of the understanding that in Python effectively all methods call self in order to access attributes belong to that class. I'm assuming from you comment that is above that this is wrong? – CXX_NVCC Jul 24 '23 at 02:18
  • 1
    Yes, that is wrong. `self` is only passed to **instance methods**, not to class methods (or static methods for that matter). – John Gordon Jul 24 '23 at 02:21

1 Answers1

0

For class instance methods, there is an automatic first argument supplied when the method is called, which is the class instance. By convention, it is named self.

def display(self) -> None:
    print(f'Student ID: {self.__student_id}')
    print(f'Student Name: {self.__student_name}'

For classmethods, a different first argument is passed -- it is the class, not the instance. By convention, it is named cls.

@classmethod
def display(cls) -> None:
    print(f'Student ID: {cls.__student_id}')
    print(f'Student Name: {cls.__student_name}'

In your code, you uncommented the @classmethod decorator but you kept the name self for the argument, which is technically allowed but is very misleading, because it means something quite different in the context of a classmethod.

John Gordon
  • 29,573
  • 7
  • 33
  • 58
  • Okay given the distinction between class methods, instance methods, static methods my question then becomes why? If we can write classes ignorant of such specifications (i.e. anecdotally I have gone by so far never using them without issue), why bother with the introduction of the title parameters which define class methods into such categories. It seems to add complexity without significant benefit. – CXX_NVCC Jul 24 '23 at 02:26
  • @CXX_NVCC If a class has a class attribute and instance attribute of the same name (as your example does), and you want a method that interacts with only the class attribute, a classmethod is the best way to do that. – John Gordon Jul 24 '23 at 02:33
  • Okay, so when creating classes in python don't declare the attributes separate from the __init__ unless you explicitly want to define class attributes. We should default on the __init__ attributes when writing attributes that are always dependent on the class instance? – CXX_NVCC Jul 24 '23 at 02:40
  • @CXX_NVCC In nearly all of the code I have written, the attributes are set in `__init__` and there are no class attributes at all. Sure, there are appropriate times to use class attributes, but it's uncommon. And it's even more uncommon to have class and instance attributes of the same name. – John Gordon Jul 24 '23 at 02:46