Inner class function without self

Let me explain with an example:

class TestClass(object):
  def __init__(self):
    self.arg = "arg"

  def test1():
    print("class method test1, Hey test")

  @classmethod
  def test2(cls):
    print("class method test2, Hey test")

  def test3(self):
    print("instance method test3, Hey test")

Look what happens when you call test1 with the class or with the instance:

First:

  TestClass.test1() #called from class
class method test1, Hey test
   TestClass().test1() #created an instance TestClass()
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: test1() takes 0 positional arguments but 1 was given

that's because when you create an instance, the self parameter is used, but here, the method has not the self parameter, that's why it brakes.

next one!

   TestClass.test2()
class method test2, Hey test
   TestClass().test2()
class method test2, Hey test

That worked for instance and for class, why? well, as you can see test2(cls) take an argument, cls, here, I'm not using it, so, it's ok that it works.

bring me the next subject, muajaja

  TestClass().test3()
instance method test3, Hey test
   TestClass.test3()
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: test3() missing 1 required positional argument: 'self'

That's easy to see, when you call it as class, you haven't provided the self parameter


In python 3, there is no difference between a function and a function defined in a class:

def test():
    print("Hey test")

class TestClass:
    def test():
        print("Hey test")

test() # works
TestClass.test() # also works

Both of these are normal functions.

The magic of the implicit self argument happens when you access a function through an instance of the class, like this:

obj = TestClass()
obj.test() # throws an error because the test function doesn't accept arguments

This is when the function test is turned into the (bound) method test. You can see the difference if you print them:

print(TestClass.test) 
print(instance.test)
# output:
# <function TestClass.test at 0xaaaaaa>
# <bound method TestClass.test of <__main__.TestClass object at 0xbbbbbb>>

To sum it up:

  • Accessing a function through the class gives you the original function.
  • Accessing a function through an instance gives you a method with a bound self argument.

For details about how exactly this conversion from function to bound method works, see the descriptor how-to, and specifically the section about functions.