[python] What is the purpose of class methods?

What just hit me, coming from Ruby, is that a so-called class method and a so-called instance method is just a function with semantic meaning applied to its first parameter, which is silently passed when the function is called as a method of an object (i.e. obj.meth()).

Normally that object must be an instance but the @classmethod method decorator changes the rules to pass a class. You can call a class method on an instance (it's just a function) - the first argument will be its class.

Because it's just a function, it can only be declared once in any given scope (i.e. class definition). If follows therefore, as a surprise to a Rubyist, that you can't have a class method and an instance method with the same name.

Consider this:

class Foo():
  def foo(x):
    print(x)

You can call foo on an instance

Foo().foo()
<__main__.Foo instance at 0x7f4dd3e3bc20>

But not on a class:

Foo.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)

Now add @classmethod:

class Foo():
  @classmethod
  def foo(x):
    print(x)

Calling on an instance now passes its class:

Foo().foo()
__main__.Foo

as does calling on a class:

Foo.foo()
__main__.Foo

It's only convention that dictates that we use self for that first argument on an instance method and cls on a class method. I used neither here to illustrate that it's just an argument. In Ruby, self is a keyword.

Contrast with Ruby:

class Foo
  def foo()
    puts "instance method #{self}"
  end
  def self.foo()
    puts "class method #{self}"
  end
end

Foo.foo()
class method Foo

Foo.new.foo()
instance method #<Foo:0x000000020fe018>

The Python class method is just a decorated function and you can use the same techniques to create your own decorators. A decorated method wraps the real method (in the case of @classmethod it passes the additional class argument). The underlying method is still there, hidden but still accessible.


footnote: I wrote this after a name clash between a class and instance method piqued my curiosity. I am far from a Python expert and would like comments if any of this is wrong.