Question: i try at the bellow code (for educational reasons) to implement this logic with class methods. What i observe is that, (a) the func1 inside the NO_CALCULATE list is not at the same address as func1 of the Parent class. (b) The type of the elements inside Calculate list, are class method objects and not methods as at CALCULATE LIST. The result of this, my code evaluates as TRUE the if statement and calculates all the functions. Can someone explain me the (a),(b) and the overall behaviour as well? (reminder:I know how to implement that logic without class methods but i try to do it that way for educational purposes)  class Parent: @classmethod def func1(cls): print("hello func1") @classmethod def func2(cls): print("hello func2") @classmethod def func3(cls): print("hello func3") CALCULATE = [func1, func2, func3] NO_CALCULATE = [] @classmethod def calculate_kpis(cls): for func in cls.CALCULATE: if func not in cls.NO_CALCULATE: func.__get__(cls)() class Child(Parent): NO_CALCULATE = [Parent.func1] #REMOVE ΤΗIS CALCULATION FROM CHILD if __name__ == "__main__": p1 = Child() p1.calculate_kpis() 

Answer: What do you mean by "address"? In Python we don't use object addresses at all. There is, yes, an object ID, that as an implementation detail is the object pointer, (its address), but it is not usable as such from pure Python code. So, assuming you are seeing the ID's: both classmethods and ordinary instance methods are dynamic objects, and they are created new when they are retrieved. The underlying function-object that is used for each method is the same object, always - but when we use a Parent.func3 expression to retrieve the func3 classmethod, Python triggers an underlying mechanism (see write ups for "Python's descriptor protocol", if you want to dig on that), which creates a new "method object" which is bound to the target class (or instance, in the case of normal methods). This object is not cached - that is also an implementation detail, so each time it is retrieved, a new one is created. One can check these assertions by either comparing their ID, or using the is operator. Pasting directly from my interactive interpreter:  In [56]: class Parent: ...: @classmethod ...: def func1(cls): ...: ... ...: In [57]: class Child(Parent): ...: pass ...: In [58]: id(Parent.func1) Out[58]: 139726686424000 In [59]: id(Parent.func1) Out[59]: 139726691941312 In [60]: id(Child.func1) Out[60]: 139727237259712 In [61]: Parent.func1 is Parent.func1 Out[61]: False In [62]: Parent.func1.__func__ is Child.func1.__func_ ...: _ Out[62]: True  As you can see, methods are always different objects, but the underlying function, reachable through the __func__ attribute in a method, is the same. This explains why your logic for "No_CALCULATE" does not work: methods lack a custom equality comparison, and then Python defaults to identity (is): and the class method in the NO_CALCULATE list is does not have the same identity as the one retrieved dynamically inside the calculate_kpis for loop. The remedy would be to compare by the __func__ attribute of each method, or rather, by the method name, as a string: that will prevent methods defined in other super-classes (such as mixins or intermediary classes) from running as well: ... class Parent: ... @classmethod def calculate_kpis(cls): for func in cls.CALCULATE: # compares using the method/function name, as string. if func.__name__ not in cls.NO_CALCULATE: # No need to use the descriptor inner method # `__get__`: the first parameter can be filled # in as a positional parameter normally func(cls) # (rather than func.__get__(cls)()) ... class Child(Parent): NO_CALCULATE = ["func1"] #List by method name, as string. ...