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












30















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|improve this question





























    30















    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|improve this question



























      30












      30








      30


      2






      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|improve this question
















      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 type-conversion implicit-conversion magic-methods






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Feb 20 at 4:30









      Solomon Ucko

      7742822




      7742822










      asked Feb 19 at 22:31









      dopplerdoppler

      34819




      34819
























          2 Answers
          2






          active

          oldest

          votes


















          38














          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





















          • 9





            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
            Feb 19 at 22:37











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

            – doppler
            Feb 19 at 22:41






          • 5





            @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
            Feb 19 at 22:42











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

            – doppler
            Feb 19 at 22:44






          • 4





            @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
            Feb 19 at 22:52



















          8














          @user2357112 already said it well but there's 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, 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









            38














            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





















            • 9





              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
              Feb 19 at 22:37











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

              – doppler
              Feb 19 at 22:41






            • 5





              @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
              Feb 19 at 22:42











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

              – doppler
              Feb 19 at 22:44






            • 4





              @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
              Feb 19 at 22:52
















            38














            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





















            • 9





              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
              Feb 19 at 22:37











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

              – doppler
              Feb 19 at 22:41






            • 5





              @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
              Feb 19 at 22:42











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

              – doppler
              Feb 19 at 22:44






            • 4





              @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
              Feb 19 at 22:52














            38












            38








            38







            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 Feb 19 at 22:38

























            answered Feb 19 at 22:35









            user2357112user2357112

            157k12172266




            157k12172266








            • 9





              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
              Feb 19 at 22:37











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

              – doppler
              Feb 19 at 22:41






            • 5





              @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
              Feb 19 at 22:42











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

              – doppler
              Feb 19 at 22:44






            • 4





              @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
              Feb 19 at 22:52














            • 9





              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
              Feb 19 at 22:37











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

              – doppler
              Feb 19 at 22:41






            • 5





              @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
              Feb 19 at 22:42











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

              – doppler
              Feb 19 at 22:44






            • 4





              @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
              Feb 19 at 22:52








            9




            9





            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
            Feb 19 at 22:37





            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
            Feb 19 at 22:37













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

            – doppler
            Feb 19 at 22:41





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

            – doppler
            Feb 19 at 22:41




            5




            5





            @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
            Feb 19 at 22:42





            @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
            Feb 19 at 22:42













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

            – doppler
            Feb 19 at 22:44





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

            – doppler
            Feb 19 at 22:44




            4




            4





            @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
            Feb 19 at 22:52





            @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
            Feb 19 at 22:52













            8














            @user2357112 already said it well but there's 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, a TypeError is raised.






            share|improve this answer






























              8














              @user2357112 already said it well but there's 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, a TypeError is raised.






              share|improve this answer




























                8












                8








                8







                @user2357112 already said it well but there's 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, a TypeError is raised.






                share|improve this answer















                @user2357112 already said it well but there's 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, a TypeError is raised.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Feb 20 at 8:08

























                answered Feb 20 at 3:03









                coldspeedcoldspeed

                137k24151237




                137k24151237






























                    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

                    Index of /

                    Tribalistas

                    Listed building