Why does calling Python's 'magic method' not do type conversion like it would for the corresponding operator?












11















When I subtract a float from an integer (e.g. 1-2.0), Python does implicit type conversion (I think). But when I call what I thought was the same operation using the magic method __sub__, it suddenly does not anymore.



What am I missing here? When I overload operators for my own classes, is there a way around this other than explicitly casting input to whatever type I need?



a=1
a.__sub__(2.)
# returns NotImplemented
a.__rsub__(2.)
# returns NotImplemented
# yet, of course:
a-2.
# returns -1.0








share



























    11















    When I subtract a float from an integer (e.g. 1-2.0), Python does implicit type conversion (I think). But when I call what I thought was the same operation using the magic method __sub__, it suddenly does not anymore.



    What am I missing here? When I overload operators for my own classes, is there a way around this other than explicitly casting input to whatever type I need?



    a=1
    a.__sub__(2.)
    # returns NotImplemented
    a.__rsub__(2.)
    # returns NotImplemented
    # yet, of course:
    a-2.
    # returns -1.0








    share

























      11












      11








      11








      When I subtract a float from an integer (e.g. 1-2.0), Python does implicit type conversion (I think). But when I call what I thought was the same operation using the magic method __sub__, it suddenly does not anymore.



      What am I missing here? When I overload operators for my own classes, is there a way around this other than explicitly casting input to whatever type I need?



      a=1
      a.__sub__(2.)
      # returns NotImplemented
      a.__rsub__(2.)
      # returns NotImplemented
      # yet, of course:
      a-2.
      # returns -1.0








      share














      When I subtract a float from an integer (e.g. 1-2.0), Python does implicit type conversion (I think). But when I call what I thought was the same operation using the magic method __sub__, it suddenly does not anymore.



      What am I missing here? When I overload operators for my own classes, is there a way around this other than explicitly casting input to whatever type I need?



      a=1
      a.__sub__(2.)
      # returns NotImplemented
      a.__rsub__(2.)
      # returns NotImplemented
      # yet, of course:
      a-2.
      # returns -1.0






      python casting





      share












      share










      share



      share










      asked 5 hours ago









      dopplerdoppler

      1467




      1467
























          2 Answers
          2






          active

          oldest

          votes


















          16














          a - b isn't just a.__sub__(b). It also tries b.__rsub__(a) if a can't handle the operation, and in the 1 - 2. case, it's the float's __rsub__ that handles the operation.



          >>> (2.).__rsub__(1)
          -1.0


          You ran a.__rsub__(2.), but that's the wrong __rsub__. You need the right-side operand's __rsub__, not the left-side operand.





          There is no implicit type conversion built into the subtraction operator. float.__rsub__ has to handle ints manually. If you want type conversion in your own operator implementations, you'll have to handle that manually too.






          share|improve this answer





















          • 6





            It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

            – Blckknght
            5 hours ago











          • Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

            – doppler
            5 hours ago






          • 2





            @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

            – user2357112
            4 hours ago











          • @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

            – doppler
            4 hours ago






          • 2





            @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

            – user2357112
            4 hours ago



















          1














          @user2357112 already said it well but nothing like an example.



          class A:
          def __sub__(self, other):
          print('A.__sub__')
          if not isinstance(other, A):
          return NotImplemented
          return 0

          def __rsub__(self, other):
          print('A.__rsub__')
          if not isinstance(other, A):
          return NotImplemented
          return 0

          class B:
          def __sub__(self, other):
          print('B.__sub__')
          if not isinstance(other, B):
          return NotImplemented
          return 0




          a1 = A()
          a2 = A()
          b = B()

          a1 - a2
          A.__sub__
          # 0


          Objects a1 and a2 are compatible (both type A), a valid result is returned.



          Next, consider,



          b - a1
          B.__sub__
          A.__rsub__
          # TypeError: unsupported operand type(s) for -: 'B' and 'A'


          Objects b and a1 are not compatible. First, b.__sub__ is tried, which returns NotImplemented, so a1.__rsub__ is tried, which also returns NotImplemented. So a TypeError is raised.



          Finally,



          a1 - b
          A.__sub__
          # TypeError: unsupported operand type(s) for -: 'A' and 'B'


          This time, a1.__sub__ is tried first, which returns NotImplemented. Now, since b.__rsub__ is not defined, so a TypeError is raised.






          share|improve this answer























            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%2f54775946%2fwhy-does-calling-pythons-magic-method-not-do-type-conversion-like-it-would-fo%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            16














            a - b isn't just a.__sub__(b). It also tries b.__rsub__(a) if a can't handle the operation, and in the 1 - 2. case, it's the float's __rsub__ that handles the operation.



            >>> (2.).__rsub__(1)
            -1.0


            You ran a.__rsub__(2.), but that's the wrong __rsub__. You need the right-side operand's __rsub__, not the left-side operand.





            There is no implicit type conversion built into the subtraction operator. float.__rsub__ has to handle ints manually. If you want type conversion in your own operator implementations, you'll have to handle that manually too.






            share|improve this answer





















            • 6





              It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

              – Blckknght
              5 hours ago











            • Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

              – doppler
              5 hours ago






            • 2





              @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

              – user2357112
              4 hours ago











            • @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

              – doppler
              4 hours ago






            • 2





              @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

              – user2357112
              4 hours ago
















            16














            a - b isn't just a.__sub__(b). It also tries b.__rsub__(a) if a can't handle the operation, and in the 1 - 2. case, it's the float's __rsub__ that handles the operation.



            >>> (2.).__rsub__(1)
            -1.0


            You ran a.__rsub__(2.), but that's the wrong __rsub__. You need the right-side operand's __rsub__, not the left-side operand.





            There is no implicit type conversion built into the subtraction operator. float.__rsub__ has to handle ints manually. If you want type conversion in your own operator implementations, you'll have to handle that manually too.






            share|improve this answer





















            • 6





              It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

              – Blckknght
              5 hours ago











            • Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

              – doppler
              5 hours ago






            • 2





              @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

              – user2357112
              4 hours ago











            • @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

              – doppler
              4 hours ago






            • 2





              @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

              – user2357112
              4 hours ago














            16












            16








            16







            a - b isn't just a.__sub__(b). It also tries b.__rsub__(a) if a can't handle the operation, and in the 1 - 2. case, it's the float's __rsub__ that handles the operation.



            >>> (2.).__rsub__(1)
            -1.0


            You ran a.__rsub__(2.), but that's the wrong __rsub__. You need the right-side operand's __rsub__, not the left-side operand.





            There is no implicit type conversion built into the subtraction operator. float.__rsub__ has to handle ints manually. If you want type conversion in your own operator implementations, you'll have to handle that manually too.






            share|improve this answer















            a - b isn't just a.__sub__(b). It also tries b.__rsub__(a) if a can't handle the operation, and in the 1 - 2. case, it's the float's __rsub__ that handles the operation.



            >>> (2.).__rsub__(1)
            -1.0


            You ran a.__rsub__(2.), but that's the wrong __rsub__. You need the right-side operand's __rsub__, not the left-side operand.





            There is no implicit type conversion built into the subtraction operator. float.__rsub__ has to handle ints manually. If you want type conversion in your own operator implementations, you'll have to handle that manually too.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited 5 hours ago

























            answered 5 hours ago









            user2357112user2357112

            155k12163258




            155k12163258








            • 6





              It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

              – Blckknght
              5 hours ago











            • Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

              – doppler
              5 hours ago






            • 2





              @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

              – user2357112
              4 hours ago











            • @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

              – doppler
              4 hours ago






            • 2





              @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

              – user2357112
              4 hours ago














            • 6





              It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

              – Blckknght
              5 hours ago











            • Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

              – doppler
              5 hours ago






            • 2





              @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

              – user2357112
              4 hours ago











            • @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

              – doppler
              4 hours ago






            • 2





              @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

              – user2357112
              4 hours ago








            6




            6





            It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

            – Blckknght
            5 hours ago





            It's worth noting that the NotImplemented result that is returned by the calls in the question are the signal to try the reverse method.

            – Blckknght
            5 hours ago













            Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

            – doppler
            5 hours ago





            Thanks! I was aware it would try __rsub__ but didn't know it would reverse the argument order.

            – doppler
            5 hours ago




            2




            2





            @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

            – user2357112
            4 hours ago





            @doppler: It'd be pretty pointless to have the left operand handle both __sub__ and __rsub__. That'd just be two methods with the exact same job, and the right operand would have no opportunity to supply an implementation.

            – user2357112
            4 hours ago













            @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

            – doppler
            4 hours ago





            @user2357112 so self.__rsub__(other) really just calls other.__sub__(self), if that makes any sense?

            – doppler
            4 hours ago




            2




            2





            @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

            – user2357112
            4 hours ago





            @doppler: No. self.__rsub__(other) is called for other - self if other can't handle it. Calling other.__sub__(self) would be pointless. We already know other can't handle it.

            – user2357112
            4 hours ago













            1














            @user2357112 already said it well but nothing like an example.



            class A:
            def __sub__(self, other):
            print('A.__sub__')
            if not isinstance(other, A):
            return NotImplemented
            return 0

            def __rsub__(self, other):
            print('A.__rsub__')
            if not isinstance(other, A):
            return NotImplemented
            return 0

            class B:
            def __sub__(self, other):
            print('B.__sub__')
            if not isinstance(other, B):
            return NotImplemented
            return 0




            a1 = A()
            a2 = A()
            b = B()

            a1 - a2
            A.__sub__
            # 0


            Objects a1 and a2 are compatible (both type A), a valid result is returned.



            Next, consider,



            b - a1
            B.__sub__
            A.__rsub__
            # TypeError: unsupported operand type(s) for -: 'B' and 'A'


            Objects b and a1 are not compatible. First, b.__sub__ is tried, which returns NotImplemented, so a1.__rsub__ is tried, which also returns NotImplemented. So a TypeError is raised.



            Finally,



            a1 - b
            A.__sub__
            # TypeError: unsupported operand type(s) for -: 'A' and 'B'


            This time, a1.__sub__ is tried first, which returns NotImplemented. Now, since b.__rsub__ is not defined, so a TypeError is raised.






            share|improve this answer




























              1














              @user2357112 already said it well but nothing like an example.



              class A:
              def __sub__(self, other):
              print('A.__sub__')
              if not isinstance(other, A):
              return NotImplemented
              return 0

              def __rsub__(self, other):
              print('A.__rsub__')
              if not isinstance(other, A):
              return NotImplemented
              return 0

              class B:
              def __sub__(self, other):
              print('B.__sub__')
              if not isinstance(other, B):
              return NotImplemented
              return 0




              a1 = A()
              a2 = A()
              b = B()

              a1 - a2
              A.__sub__
              # 0


              Objects a1 and a2 are compatible (both type A), a valid result is returned.



              Next, consider,



              b - a1
              B.__sub__
              A.__rsub__
              # TypeError: unsupported operand type(s) for -: 'B' and 'A'


              Objects b and a1 are not compatible. First, b.__sub__ is tried, which returns NotImplemented, so a1.__rsub__ is tried, which also returns NotImplemented. So a TypeError is raised.



              Finally,



              a1 - b
              A.__sub__
              # TypeError: unsupported operand type(s) for -: 'A' and 'B'


              This time, a1.__sub__ is tried first, which returns NotImplemented. Now, since b.__rsub__ is not defined, so a TypeError is raised.






              share|improve this answer


























                1












                1








                1







                @user2357112 already said it well but nothing like an example.



                class A:
                def __sub__(self, other):
                print('A.__sub__')
                if not isinstance(other, A):
                return NotImplemented
                return 0

                def __rsub__(self, other):
                print('A.__rsub__')
                if not isinstance(other, A):
                return NotImplemented
                return 0

                class B:
                def __sub__(self, other):
                print('B.__sub__')
                if not isinstance(other, B):
                return NotImplemented
                return 0




                a1 = A()
                a2 = A()
                b = B()

                a1 - a2
                A.__sub__
                # 0


                Objects a1 and a2 are compatible (both type A), a valid result is returned.



                Next, consider,



                b - a1
                B.__sub__
                A.__rsub__
                # TypeError: unsupported operand type(s) for -: 'B' and 'A'


                Objects b and a1 are not compatible. First, b.__sub__ is tried, which returns NotImplemented, so a1.__rsub__ is tried, which also returns NotImplemented. So a TypeError is raised.



                Finally,



                a1 - b
                A.__sub__
                # TypeError: unsupported operand type(s) for -: 'A' and 'B'


                This time, a1.__sub__ is tried first, which returns NotImplemented. Now, since b.__rsub__ is not defined, so a TypeError is raised.






                share|improve this answer













                @user2357112 already said it well but nothing like an example.



                class A:
                def __sub__(self, other):
                print('A.__sub__')
                if not isinstance(other, A):
                return NotImplemented
                return 0

                def __rsub__(self, other):
                print('A.__rsub__')
                if not isinstance(other, A):
                return NotImplemented
                return 0

                class B:
                def __sub__(self, other):
                print('B.__sub__')
                if not isinstance(other, B):
                return NotImplemented
                return 0




                a1 = A()
                a2 = A()
                b = B()

                a1 - a2
                A.__sub__
                # 0


                Objects a1 and a2 are compatible (both type A), a valid result is returned.



                Next, consider,



                b - a1
                B.__sub__
                A.__rsub__
                # TypeError: unsupported operand type(s) for -: 'B' and 'A'


                Objects b and a1 are not compatible. First, b.__sub__ is tried, which returns NotImplemented, so a1.__rsub__ is tried, which also returns NotImplemented. So a TypeError is raised.



                Finally,



                a1 - b
                A.__sub__
                # TypeError: unsupported operand type(s) for -: 'A' and 'B'


                This time, a1.__sub__ is tried first, which returns NotImplemented. Now, since b.__rsub__ is not defined, so a TypeError is raised.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 38 mins ago









                coldspeedcoldspeed

                132k23139223




                132k23139223






























                    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%2f54775946%2fwhy-does-calling-pythons-magic-method-not-do-type-conversion-like-it-would-fo%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

                    濃尾地震

                    How to rewrite equation of hyperbola in standard form

                    No ethernet ip address in my vocore2