‘Only in Python’ series talks about the constructs that are more specific to Python than other mainstream languages. Of course there are some overlaps with other language, so my choices would be language behavior/characteristics screams Python whenever I see it.
Python also access the class (not instance) by its own name (just like MATLAB) followed by dot operator. In C++, class-wise (not instance-wise) are always accessed through SRO, aka Scope Resolution Operator ::
.
There isn’t really a concept for class method despite Python has a @classmethod
decorator. The syntactic sugar merely for the use case where a static function happens to access to static members of the same class through its own class’s name.
Static methods and static data members are pretty much glorified namespaces (just like packages and modules in Python) that isn’t tied to any object instances. If a function wants to access the class-wise members (attributes/properties), simply call them by the name of the class instead of object instances of it.
Btw, C++ implicitly passes *this
internally and not rely on the function prototype, aka first argument, to pass self
around, so you don’t have this problem.
MATLAB does not have native support for static data member but it has static methods. (Btw, in MATLAB, attributes means the access modifiers to methods
and properties
block, not members of a class like in Python). It’s smart enough to figure out if a method is marked static so it’s exempt from receiving the object itself as the first argument.
Python is a little primative in this department. Calling a method from an object instances always imply passing self
as the first argument in the call, whether you want it (instance methods) or not (static/class-wise methods).
If you call a method by the class name, no implict first argument was passed.
To address this mandatory self
issue, Python simply use decorators (function wrappers) to wrestle the passed self
to fit your purposes.
@staticmethod
effectively tosses out the implied first argument (self
) passed by method calls from object instances since it’s by definition not needed/wanted.
@classmethod
effectively takes the implied first argument (self
) and replace it with its class name, aka type(self)
as cls
so your function prototype expects the first argument. I said effectively because if you call the @classmethod
-decorated method by class name, which self
is not passed, the deocrator still produces the variable cls
to match the first argument in your function prototype.
Here’s an example:
class A: a = 3 def __init__(self): self.a = 42 @classmethod def print_a_cls(cls): print(A.a) print(cls.a) # Both obj_a.print_a_cls() and A.print_a_cls() prints 3 twice # @classmethod basically swap the passed 'self' argument and replace the first arg # with type(self) which is the class's name A here @staticmethod def print_a_static(): print(A.a) # Both obj_a.print_a_static() and A.print_a_static() prints 3 # @staticmethod basically absorbs and discard the first argument 'self' if passed def print_a_instance(self): print(self.a) def print_a_classwise(): print(A.a) # obj_a.print_a_classwise() # "TypeError: A.print_a_classwise() takes 0 positional arguments but 1 was given" # A.print_a_classwise() prints 3
If you don’t use these declarators, methods without self
as the first argument will work only if you call them by the class’s name. If you try to call it through an object instance, it’ll rightfully refuse to run because the call supplies self
as an argument which your function isn’t ready to accept.