Why does calling Python's 'magic method' not do type conversion like it would for the corresponding operator?
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
add a comment |
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
add a comment |
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
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
python type-conversion implicit-conversion magic-methods
edited Feb 20 at 4:30
Solomon Ucko
7742822
7742822
asked Feb 19 at 22:31
dopplerdoppler
34819
34819
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
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.
9
It's worth noting that theNotImplementedresult 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 soself.__rsub__(other)really just callsother.__sub__(self), if that makes any sense?
– doppler
Feb 19 at 22:44
4
@doppler: No.self.__rsub__(other)is called forother - selfifothercan't handle it. Callingother.__sub__(self)would be pointless. We already knowothercan't handle it.
– user2357112
Feb 19 at 22:52
|
show 1 more comment
@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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
9
It's worth noting that theNotImplementedresult 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 soself.__rsub__(other)really just callsother.__sub__(self), if that makes any sense?
– doppler
Feb 19 at 22:44
4
@doppler: No.self.__rsub__(other)is called forother - selfifothercan't handle it. Callingother.__sub__(self)would be pointless. We already knowothercan't handle it.
– user2357112
Feb 19 at 22:52
|
show 1 more comment
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.
9
It's worth noting that theNotImplementedresult 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 soself.__rsub__(other)really just callsother.__sub__(self), if that makes any sense?
– doppler
Feb 19 at 22:44
4
@doppler: No.self.__rsub__(other)is called forother - selfifothercan't handle it. Callingother.__sub__(self)would be pointless. We already knowothercan't handle it.
– user2357112
Feb 19 at 22:52
|
show 1 more comment
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.
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.
edited Feb 19 at 22:38
answered Feb 19 at 22:35
user2357112user2357112
157k12172266
157k12172266
9
It's worth noting that theNotImplementedresult 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 soself.__rsub__(other)really just callsother.__sub__(self), if that makes any sense?
– doppler
Feb 19 at 22:44
4
@doppler: No.self.__rsub__(other)is called forother - selfifothercan't handle it. Callingother.__sub__(self)would be pointless. We already knowothercan't handle it.
– user2357112
Feb 19 at 22:52
|
show 1 more comment
9
It's worth noting that theNotImplementedresult 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 soself.__rsub__(other)really just callsother.__sub__(self), if that makes any sense?
– doppler
Feb 19 at 22:44
4
@doppler: No.self.__rsub__(other)is called forother - selfifothercan't handle it. Callingother.__sub__(self)would be pointless. We already knowothercan'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
|
show 1 more comment
@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.
add a comment |
@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.
add a comment |
@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.
@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.
edited Feb 20 at 8:08
answered Feb 20 at 3:03
coldspeedcoldspeed
137k24151237
137k24151237
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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