Inner class function without self












6















Peace, everyone!
I'm using Python 3.6.3 and I find strange that such construction is possible:



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

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


And using:



>>> TestClass.test()
"Hey test"


I know that in Python there are standard methods with self as parameter (don't know how to call them properly), static methods, class methods, abstract methods.



But what kind of method the test() is?
Is it static method?





Edited:



Are there any useful usecases of such method of determining a function inside a class?










share|improve this question




















  • 2





    why do you find that code strange?

    – mast3rd3mon
    Nov 1 '17 at 11:52











  • test is a function taking no parameters and you're calling it without parameters. All good. Now, TestClass().test() OTOH is a different story…

    – deceze
    Nov 1 '17 at 11:53






  • 3





    Yes. It is very weird that it changed "Hey" to lowercase.

    – stark
    Nov 1 '17 at 11:55






  • 3





    Is the ambiguity of self.test a part of the question, or just a coincidence?

    – tobias_k
    Nov 1 '17 at 12:00











  • @tobias_k sorry, it's just coincidence, i fix it

    – Gusev Slava
    Nov 1 '17 at 12:01
















6















Peace, everyone!
I'm using Python 3.6.3 and I find strange that such construction is possible:



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

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


And using:



>>> TestClass.test()
"Hey test"


I know that in Python there are standard methods with self as parameter (don't know how to call them properly), static methods, class methods, abstract methods.



But what kind of method the test() is?
Is it static method?





Edited:



Are there any useful usecases of such method of determining a function inside a class?










share|improve this question




















  • 2





    why do you find that code strange?

    – mast3rd3mon
    Nov 1 '17 at 11:52











  • test is a function taking no parameters and you're calling it without parameters. All good. Now, TestClass().test() OTOH is a different story…

    – deceze
    Nov 1 '17 at 11:53






  • 3





    Yes. It is very weird that it changed "Hey" to lowercase.

    – stark
    Nov 1 '17 at 11:55






  • 3





    Is the ambiguity of self.test a part of the question, or just a coincidence?

    – tobias_k
    Nov 1 '17 at 12:00











  • @tobias_k sorry, it's just coincidence, i fix it

    – Gusev Slava
    Nov 1 '17 at 12:01














6












6








6








Peace, everyone!
I'm using Python 3.6.3 and I find strange that such construction is possible:



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

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


And using:



>>> TestClass.test()
"Hey test"


I know that in Python there are standard methods with self as parameter (don't know how to call them properly), static methods, class methods, abstract methods.



But what kind of method the test() is?
Is it static method?





Edited:



Are there any useful usecases of such method of determining a function inside a class?










share|improve this question
















Peace, everyone!
I'm using Python 3.6.3 and I find strange that such construction is possible:



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

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


And using:



>>> TestClass.test()
"Hey test"


I know that in Python there are standard methods with self as parameter (don't know how to call them properly), static methods, class methods, abstract methods.



But what kind of method the test() is?
Is it static method?





Edited:



Are there any useful usecases of such method of determining a function inside a class?







python python-3.x class






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 23 '18 at 7:08









thepurpleowl

99112




99112










asked Nov 1 '17 at 11:50









Gusev SlavaGusev Slava

779716




779716








  • 2





    why do you find that code strange?

    – mast3rd3mon
    Nov 1 '17 at 11:52











  • test is a function taking no parameters and you're calling it without parameters. All good. Now, TestClass().test() OTOH is a different story…

    – deceze
    Nov 1 '17 at 11:53






  • 3





    Yes. It is very weird that it changed "Hey" to lowercase.

    – stark
    Nov 1 '17 at 11:55






  • 3





    Is the ambiguity of self.test a part of the question, or just a coincidence?

    – tobias_k
    Nov 1 '17 at 12:00











  • @tobias_k sorry, it's just coincidence, i fix it

    – Gusev Slava
    Nov 1 '17 at 12:01














  • 2





    why do you find that code strange?

    – mast3rd3mon
    Nov 1 '17 at 11:52











  • test is a function taking no parameters and you're calling it without parameters. All good. Now, TestClass().test() OTOH is a different story…

    – deceze
    Nov 1 '17 at 11:53






  • 3





    Yes. It is very weird that it changed "Hey" to lowercase.

    – stark
    Nov 1 '17 at 11:55






  • 3





    Is the ambiguity of self.test a part of the question, or just a coincidence?

    – tobias_k
    Nov 1 '17 at 12:00











  • @tobias_k sorry, it's just coincidence, i fix it

    – Gusev Slava
    Nov 1 '17 at 12:01








2




2





why do you find that code strange?

– mast3rd3mon
Nov 1 '17 at 11:52





why do you find that code strange?

– mast3rd3mon
Nov 1 '17 at 11:52













test is a function taking no parameters and you're calling it without parameters. All good. Now, TestClass().test() OTOH is a different story…

– deceze
Nov 1 '17 at 11:53





test is a function taking no parameters and you're calling it without parameters. All good. Now, TestClass().test() OTOH is a different story…

– deceze
Nov 1 '17 at 11:53




3




3





Yes. It is very weird that it changed "Hey" to lowercase.

– stark
Nov 1 '17 at 11:55





Yes. It is very weird that it changed "Hey" to lowercase.

– stark
Nov 1 '17 at 11:55




3




3





Is the ambiguity of self.test a part of the question, or just a coincidence?

– tobias_k
Nov 1 '17 at 12:00





Is the ambiguity of self.test a part of the question, or just a coincidence?

– tobias_k
Nov 1 '17 at 12:00













@tobias_k sorry, it's just coincidence, i fix it

– Gusev Slava
Nov 1 '17 at 12:01





@tobias_k sorry, it's just coincidence, i fix it

– Gusev Slava
Nov 1 '17 at 12:01












5 Answers
5






active

oldest

votes


















3














In Python 3, (unlike Python 2) a function accessed and called from the class is just another function; nothing special:




Note that the transformation from function object to instance method
object happens each time the attribute is retrieved from the instance.




[Emphasis mine]



You just happened to be calling the function with the right set of parameters albeit accessed via the class object. Same as calling the underlying function object for the method via an instance:



TestClass().test.__func__() # "Hey test"


A quick test explains it further:



print(TestClass().test is TestClass.test)
# False
print(TestClass().test.__func__ is TestClass.test)
# True


However, in Python 2, the behaviour is different as the transformation from function object to method object happens when the attribute is accessed via both the class or instance:




Note that the transformation from function object to (unbound or
bound) method object happens each time the attribute is retrieved from
the class or instance.




[Emphasis mine]






share|improve this answer

































    3














    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






    share|improve this answer

































      2














      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 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






      share|improve this answer































        1














        Python 3 dispensed with the distinction between a bound and unbound method that existed in Python 2. What was previously an unbound method is now just a regular function.



        class A(object):
        def test():
        pass


        In Python 2:



        >>> A.test
        <unbound method A.test>


        whereas in Python 3:



        >>> A.test
        <function A.test at 0x101cbbae8>


        (the address may differ)



        test here is a descriptor; you don't (necessarily) get back the original function object when you access A.test; instead you get the return value of that object's (i.e. the function's) __get__ method, which is called with two arguments. Which arguments depends on whether you access it via the class or via an instance of a class.





        • A.test => A.test.__get__(None, A)


        • a = A(); a.test => A.test.__get__(a, A)


        In Python 2, A.test.__get__(None, A) returns an method object, which is a wrapper around the original function. As an unbound method, the wrapper still expects an instance of A as its first argument, even though the function itself was not defined with any parameters.



        In Python 3, however, A.test.__get(None, A) simply returns a reference to the original function, not a method object. As a result, you can use it exactly as you defined it in the first place.



        You can confirm this by examining id(A.__dict__['test']) and id(A.test) in Python 2 and Python 3. In Python 2, you'll get two different values; in Python 3, you'll get the same value for each.






        share|improve this answer

































          0














          TestClass.test() call actually executes the test() on the class object. This is similar to a @staticmethod (a method that can be executed on the class object, without creating an object first).



          ins = TestClass()
          ins.test()


          will throw an exception, since instance methods pass self as the first argument, and test() takes no args.



          When an object is created, the methods defined in the clas are bound to it. They are actually different objects, hence they have different ids:



             id(TestClass.test)
          => 140288592901800
          obj = TestClass()
          id(obj.test)
          => 140288605765960


          In Python 2.7, your code throws an exception which is self explantory:



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





          share|improve this answer


























          • @staticmethod may be called from instances of the class, but in my example it isn't possible

            – Gusev Slava
            Nov 1 '17 at 12:19











          • That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

            – Chen A.
            Nov 1 '17 at 14:36











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f47054156%2finner-class-function-without-self%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          5 Answers
          5






          active

          oldest

          votes








          5 Answers
          5






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          3














          In Python 3, (unlike Python 2) a function accessed and called from the class is just another function; nothing special:




          Note that the transformation from function object to instance method
          object happens each time the attribute is retrieved from the instance.




          [Emphasis mine]



          You just happened to be calling the function with the right set of parameters albeit accessed via the class object. Same as calling the underlying function object for the method via an instance:



          TestClass().test.__func__() # "Hey test"


          A quick test explains it further:



          print(TestClass().test is TestClass.test)
          # False
          print(TestClass().test.__func__ is TestClass.test)
          # True


          However, in Python 2, the behaviour is different as the transformation from function object to method object happens when the attribute is accessed via both the class or instance:




          Note that the transformation from function object to (unbound or
          bound) method object happens each time the attribute is retrieved from
          the class or instance.




          [Emphasis mine]






          share|improve this answer






























            3














            In Python 3, (unlike Python 2) a function accessed and called from the class is just another function; nothing special:




            Note that the transformation from function object to instance method
            object happens each time the attribute is retrieved from the instance.




            [Emphasis mine]



            You just happened to be calling the function with the right set of parameters albeit accessed via the class object. Same as calling the underlying function object for the method via an instance:



            TestClass().test.__func__() # "Hey test"


            A quick test explains it further:



            print(TestClass().test is TestClass.test)
            # False
            print(TestClass().test.__func__ is TestClass.test)
            # True


            However, in Python 2, the behaviour is different as the transformation from function object to method object happens when the attribute is accessed via both the class or instance:




            Note that the transformation from function object to (unbound or
            bound) method object happens each time the attribute is retrieved from
            the class or instance.




            [Emphasis mine]






            share|improve this answer




























              3












              3








              3







              In Python 3, (unlike Python 2) a function accessed and called from the class is just another function; nothing special:




              Note that the transformation from function object to instance method
              object happens each time the attribute is retrieved from the instance.




              [Emphasis mine]



              You just happened to be calling the function with the right set of parameters albeit accessed via the class object. Same as calling the underlying function object for the method via an instance:



              TestClass().test.__func__() # "Hey test"


              A quick test explains it further:



              print(TestClass().test is TestClass.test)
              # False
              print(TestClass().test.__func__ is TestClass.test)
              # True


              However, in Python 2, the behaviour is different as the transformation from function object to method object happens when the attribute is accessed via both the class or instance:




              Note that the transformation from function object to (unbound or
              bound) method object happens each time the attribute is retrieved from
              the class or instance.




              [Emphasis mine]






              share|improve this answer















              In Python 3, (unlike Python 2) a function accessed and called from the class is just another function; nothing special:




              Note that the transformation from function object to instance method
              object happens each time the attribute is retrieved from the instance.




              [Emphasis mine]



              You just happened to be calling the function with the right set of parameters albeit accessed via the class object. Same as calling the underlying function object for the method via an instance:



              TestClass().test.__func__() # "Hey test"


              A quick test explains it further:



              print(TestClass().test is TestClass.test)
              # False
              print(TestClass().test.__func__ is TestClass.test)
              # True


              However, in Python 2, the behaviour is different as the transformation from function object to method object happens when the attribute is accessed via both the class or instance:




              Note that the transformation from function object to (unbound or
              bound) method object happens each time the attribute is retrieved from
              the class or instance.




              [Emphasis mine]







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Nov 1 '17 at 12:12

























              answered Nov 1 '17 at 11:59









              Moses KoledoyeMoses Koledoye

              62.4k75979




              62.4k75979

























                  3














                  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






                  share|improve this answer






























                    3














                    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






                    share|improve this answer




























                      3












                      3








                      3







                      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






                      share|improve this answer















                      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







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Nov 23 '18 at 7:00









                      thepurpleowl

                      99112




                      99112










                      answered Nov 1 '17 at 11:55









                      Damian LatteneroDamian Lattenero

                      9,09021944




                      9,09021944























                          2














                          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 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






                          share|improve this answer




























                            2














                            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 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






                            share|improve this answer


























                              2












                              2








                              2







                              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 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






                              share|improve this answer













                              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 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







                              share|improve this answer












                              share|improve this answer



                              share|improve this answer










                              answered Nov 1 '17 at 12:22









                              Aran-FeyAran-Fey

                              20.4k53871




                              20.4k53871























                                  1














                                  Python 3 dispensed with the distinction between a bound and unbound method that existed in Python 2. What was previously an unbound method is now just a regular function.



                                  class A(object):
                                  def test():
                                  pass


                                  In Python 2:



                                  >>> A.test
                                  <unbound method A.test>


                                  whereas in Python 3:



                                  >>> A.test
                                  <function A.test at 0x101cbbae8>


                                  (the address may differ)



                                  test here is a descriptor; you don't (necessarily) get back the original function object when you access A.test; instead you get the return value of that object's (i.e. the function's) __get__ method, which is called with two arguments. Which arguments depends on whether you access it via the class or via an instance of a class.





                                  • A.test => A.test.__get__(None, A)


                                  • a = A(); a.test => A.test.__get__(a, A)


                                  In Python 2, A.test.__get__(None, A) returns an method object, which is a wrapper around the original function. As an unbound method, the wrapper still expects an instance of A as its first argument, even though the function itself was not defined with any parameters.



                                  In Python 3, however, A.test.__get(None, A) simply returns a reference to the original function, not a method object. As a result, you can use it exactly as you defined it in the first place.



                                  You can confirm this by examining id(A.__dict__['test']) and id(A.test) in Python 2 and Python 3. In Python 2, you'll get two different values; in Python 3, you'll get the same value for each.






                                  share|improve this answer






























                                    1














                                    Python 3 dispensed with the distinction between a bound and unbound method that existed in Python 2. What was previously an unbound method is now just a regular function.



                                    class A(object):
                                    def test():
                                    pass


                                    In Python 2:



                                    >>> A.test
                                    <unbound method A.test>


                                    whereas in Python 3:



                                    >>> A.test
                                    <function A.test at 0x101cbbae8>


                                    (the address may differ)



                                    test here is a descriptor; you don't (necessarily) get back the original function object when you access A.test; instead you get the return value of that object's (i.e. the function's) __get__ method, which is called with two arguments. Which arguments depends on whether you access it via the class or via an instance of a class.





                                    • A.test => A.test.__get__(None, A)


                                    • a = A(); a.test => A.test.__get__(a, A)


                                    In Python 2, A.test.__get__(None, A) returns an method object, which is a wrapper around the original function. As an unbound method, the wrapper still expects an instance of A as its first argument, even though the function itself was not defined with any parameters.



                                    In Python 3, however, A.test.__get(None, A) simply returns a reference to the original function, not a method object. As a result, you can use it exactly as you defined it in the first place.



                                    You can confirm this by examining id(A.__dict__['test']) and id(A.test) in Python 2 and Python 3. In Python 2, you'll get two different values; in Python 3, you'll get the same value for each.






                                    share|improve this answer




























                                      1












                                      1








                                      1







                                      Python 3 dispensed with the distinction between a bound and unbound method that existed in Python 2. What was previously an unbound method is now just a regular function.



                                      class A(object):
                                      def test():
                                      pass


                                      In Python 2:



                                      >>> A.test
                                      <unbound method A.test>


                                      whereas in Python 3:



                                      >>> A.test
                                      <function A.test at 0x101cbbae8>


                                      (the address may differ)



                                      test here is a descriptor; you don't (necessarily) get back the original function object when you access A.test; instead you get the return value of that object's (i.e. the function's) __get__ method, which is called with two arguments. Which arguments depends on whether you access it via the class or via an instance of a class.





                                      • A.test => A.test.__get__(None, A)


                                      • a = A(); a.test => A.test.__get__(a, A)


                                      In Python 2, A.test.__get__(None, A) returns an method object, which is a wrapper around the original function. As an unbound method, the wrapper still expects an instance of A as its first argument, even though the function itself was not defined with any parameters.



                                      In Python 3, however, A.test.__get(None, A) simply returns a reference to the original function, not a method object. As a result, you can use it exactly as you defined it in the first place.



                                      You can confirm this by examining id(A.__dict__['test']) and id(A.test) in Python 2 and Python 3. In Python 2, you'll get two different values; in Python 3, you'll get the same value for each.






                                      share|improve this answer















                                      Python 3 dispensed with the distinction between a bound and unbound method that existed in Python 2. What was previously an unbound method is now just a regular function.



                                      class A(object):
                                      def test():
                                      pass


                                      In Python 2:



                                      >>> A.test
                                      <unbound method A.test>


                                      whereas in Python 3:



                                      >>> A.test
                                      <function A.test at 0x101cbbae8>


                                      (the address may differ)



                                      test here is a descriptor; you don't (necessarily) get back the original function object when you access A.test; instead you get the return value of that object's (i.e. the function's) __get__ method, which is called with two arguments. Which arguments depends on whether you access it via the class or via an instance of a class.





                                      • A.test => A.test.__get__(None, A)


                                      • a = A(); a.test => A.test.__get__(a, A)


                                      In Python 2, A.test.__get__(None, A) returns an method object, which is a wrapper around the original function. As an unbound method, the wrapper still expects an instance of A as its first argument, even though the function itself was not defined with any parameters.



                                      In Python 3, however, A.test.__get(None, A) simply returns a reference to the original function, not a method object. As a result, you can use it exactly as you defined it in the first place.



                                      You can confirm this by examining id(A.__dict__['test']) and id(A.test) in Python 2 and Python 3. In Python 2, you'll get two different values; in Python 3, you'll get the same value for each.







                                      share|improve this answer














                                      share|improve this answer



                                      share|improve this answer








                                      edited Nov 1 '17 at 13:23

























                                      answered Nov 1 '17 at 12:32









                                      chepnerchepner

                                      258k34249343




                                      258k34249343























                                          0














                                          TestClass.test() call actually executes the test() on the class object. This is similar to a @staticmethod (a method that can be executed on the class object, without creating an object first).



                                          ins = TestClass()
                                          ins.test()


                                          will throw an exception, since instance methods pass self as the first argument, and test() takes no args.



                                          When an object is created, the methods defined in the clas are bound to it. They are actually different objects, hence they have different ids:



                                             id(TestClass.test)
                                          => 140288592901800
                                          obj = TestClass()
                                          id(obj.test)
                                          => 140288605765960


                                          In Python 2.7, your code throws an exception which is self explantory:



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





                                          share|improve this answer


























                                          • @staticmethod may be called from instances of the class, but in my example it isn't possible

                                            – Gusev Slava
                                            Nov 1 '17 at 12:19











                                          • That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

                                            – Chen A.
                                            Nov 1 '17 at 14:36
















                                          0














                                          TestClass.test() call actually executes the test() on the class object. This is similar to a @staticmethod (a method that can be executed on the class object, without creating an object first).



                                          ins = TestClass()
                                          ins.test()


                                          will throw an exception, since instance methods pass self as the first argument, and test() takes no args.



                                          When an object is created, the methods defined in the clas are bound to it. They are actually different objects, hence they have different ids:



                                             id(TestClass.test)
                                          => 140288592901800
                                          obj = TestClass()
                                          id(obj.test)
                                          => 140288605765960


                                          In Python 2.7, your code throws an exception which is self explantory:



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





                                          share|improve this answer


























                                          • @staticmethod may be called from instances of the class, but in my example it isn't possible

                                            – Gusev Slava
                                            Nov 1 '17 at 12:19











                                          • That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

                                            – Chen A.
                                            Nov 1 '17 at 14:36














                                          0












                                          0








                                          0







                                          TestClass.test() call actually executes the test() on the class object. This is similar to a @staticmethod (a method that can be executed on the class object, without creating an object first).



                                          ins = TestClass()
                                          ins.test()


                                          will throw an exception, since instance methods pass self as the first argument, and test() takes no args.



                                          When an object is created, the methods defined in the clas are bound to it. They are actually different objects, hence they have different ids:



                                             id(TestClass.test)
                                          => 140288592901800
                                          obj = TestClass()
                                          id(obj.test)
                                          => 140288605765960


                                          In Python 2.7, your code throws an exception which is self explantory:



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





                                          share|improve this answer















                                          TestClass.test() call actually executes the test() on the class object. This is similar to a @staticmethod (a method that can be executed on the class object, without creating an object first).



                                          ins = TestClass()
                                          ins.test()


                                          will throw an exception, since instance methods pass self as the first argument, and test() takes no args.



                                          When an object is created, the methods defined in the clas are bound to it. They are actually different objects, hence they have different ids:



                                             id(TestClass.test)
                                          => 140288592901800
                                          obj = TestClass()
                                          id(obj.test)
                                          => 140288605765960


                                          In Python 2.7, your code throws an exception which is self explantory:



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






                                          share|improve this answer














                                          share|improve this answer



                                          share|improve this answer








                                          edited Nov 1 '17 at 12:02

























                                          answered Nov 1 '17 at 11:57









                                          Chen A.Chen A.

                                          5,33221634




                                          5,33221634













                                          • @staticmethod may be called from instances of the class, but in my example it isn't possible

                                            – Gusev Slava
                                            Nov 1 '17 at 12:19











                                          • That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

                                            – Chen A.
                                            Nov 1 '17 at 14:36



















                                          • @staticmethod may be called from instances of the class, but in my example it isn't possible

                                            – Gusev Slava
                                            Nov 1 '17 at 12:19











                                          • That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

                                            – Chen A.
                                            Nov 1 '17 at 14:36

















                                          @staticmethod may be called from instances of the class, but in my example it isn't possible

                                          – Gusev Slava
                                          Nov 1 '17 at 12:19





                                          @staticmethod may be called from instances of the class, but in my example it isn't possible

                                          – Gusev Slava
                                          Nov 1 '17 at 12:19













                                          That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

                                          – Chen A.
                                          Nov 1 '17 at 14:36





                                          That's because when anything is called on an instance method, the self part is passed automatically. If your function don't take any args, you simply can't execute it on an instance object.

                                          – Chen A.
                                          Nov 1 '17 at 14:36


















                                          draft saved

                                          draft discarded




















































                                          Thanks for contributing an answer to Stack Overflow!


                                          • Please be sure to answer the question. Provide details and share your research!

                                          But avoid



                                          • Asking for help, clarification, or responding to other answers.

                                          • Making statements based on opinion; back them up with references or personal experience.


                                          To learn more, see our tips on writing great answers.




                                          draft saved


                                          draft discarded














                                          StackExchange.ready(
                                          function () {
                                          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f47054156%2finner-class-function-without-self%23new-answer', 'question_page');
                                          }
                                          );

                                          Post as a guest















                                          Required, but never shown





















































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown

































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown







                                          Popular posts from this blog

                                          If I really need a card on my start hand, how many mulligans make sense? [duplicate]

                                          Alcedinidae

                                          Can an atomic nucleus contain both particles and antiparticles? [duplicate]