Are there more than three types of methods in Python?

Viewed 281

I understand there are at least 3 kinds of methods in Python having different first arguments:

  1. instance method - instance, i.e. self
  2. class method - class, i.e. cls
  3. static method - nothing

These classic methods are implemented in the Test class below including an usual method:

class Test():

    def __init__(self):
        pass

    def instance_mthd(self):
        print("Instance method.")

    @classmethod
    def class_mthd(cls):
        print("Class method.")

    @staticmethod
    def static_mthd():
        print("Static method.")

    def unknown_mthd():
        # No decoration --> instance method, but
        # No self (or cls) --> static method, so ... (?)
        print("Unknown method.")

In Python 3, the unknown_mthd can be called safely, yet it raises an error in Python 2:

>>> t = Test()

>>> # Python 3
>>> t.instance_mthd()
>>> Test.class_mthd()
>>> t.static_mthd()
>>> Test.unknown_mthd()

Instance method.
Class method.
Static method.
Unknown method.

>>> # Python 2
>>> Test.unknown_mthd()    
TypeError: unbound method unknown_mthd() must be called with Test instance as first argument (got nothing instead)

This error suggests such a method was not intended in Python 2. Perhaps its allowance now is due to the elimination of unbound methods in Python 3 (REF 001). Moreover, unknown_mthd does not accept args, and it can be bound to called by a class like a staticmethod, Test.unknown_mthd(). However, it is not an explicit staticmethod (no decorator).

Questions

  1. Was making a method this way (without args while not explicitly decorated as staticmethods) intentional in Python 3's design? UPDATED
  2. Among the classic method types, what type of method is unknown_mthd?
  3. Why can unknown_mthd be called by the class without passing an argument?

Some preliminary inspection yields inconclusive results:

>>> # Types
>>> print("i", type(t.instance_mthd))
>>> print("c", type(Test.class_mthd))
>>> print("s", type(t.static_mthd))
>>> print("u", type(Test.unknown_mthd))                             
>>> print()

>>> # __dict__ Types, REF 002
>>> print("i", type(t.__class__.__dict__["instance_mthd"]))
>>> print("c", type(t.__class__.__dict__["class_mthd"]))
>>> print("s", type(t.__class__.__dict__["static_mthd"]))
>>> print("u", type(t.__class__.__dict__["unknown_mthd"]))          
>>> print()

i <class 'method'>
c <class 'method'>
s <class 'function'>
u <class 'function'>

i <class 'function'>
c <class 'classmethod'>
s <class 'staticmethod'>
u <class 'function'>

The first set of type inspections suggests unknown_mthd is something similar to a staticmethod. The second suggests it resembles an instance method. I'm not sure what this method is or why it should be used over the classic ones. I would appreciate some advice on how to inspect and understand it better. Thanks.

3 Answers
Related