Why is nb_inplace_power ternary?
#1653736 reports that slot_nb_inplace_power has the wrong type: it should be a ternary function, but only is a binary. The proposed change is to make it ternary, and to invoke __ipow__ with three arguments. In researching this, I came to wonder why nb_inplace_power is ternary in the first place. It is the implementation of foo **= bar (and that's its only use), so it ought to be binary. Historically, the reason probably is that it was copied from nb_power, which is ternary because it also implements pow()). So here is my proposed change: 1. For 2.5.1, rewrite slot_nb_inplace_power to raise an exception if the third argument is not None, and then invoke __ipow__ with only one argument. 2. For 2.6, change the API to make nb_inplace_power binary. Optionally, in 2.5, the exception could be added into other places as well, e.g. PyNumber_InPlacePower and instance_ipow (rather than invoking __ipow__ with a third argument if its not None). Comments? Regards, Martin
On 2/8/07, "Martin v. Löwis" <Martin.vonLoewis@hpi.uni-potsdam.de> wrote:
#1653736 reports that slot_nb_inplace_power has the wrong type: it should be a ternary function, but only is a binary. The proposed change is to make it ternary, and to invoke __ipow__ with three arguments.
In researching this, I came to wonder why nb_inplace_power is ternary in the first place. It is the implementation of
foo **= bar
(and that's its only use), so it ought to be binary. Historically, the reason probably is that it was copied from nb_power, which is ternary because it also implements pow()).
So here is my proposed change:
1. For 2.5.1, rewrite slot_nb_inplace_power to raise an exception if the third argument is not None, and then invoke __ipow__ with only one argument.
2. For 2.6, change the API to make nb_inplace_power binary.
Optionally, in 2.5, the exception could be added into other places as well, e.g. PyNumber_InPlacePower and instance_ipow (rather than invoking __ipow__ with a third argument if its not None).
Comments?
Seems reasonable to me. Is the argument of None passed in automatically somewhere? I think the 2.6 change is definitely good, just don't know if the exception is really needed. I realize the signature is off, but it doesn't hurt anyone at this point if it stayed wrong since obviously the semantics would be the same as they were. -Brett
Brett Cannon schrieb:
Seems reasonable to me. Is the argument of None passed in automatically somewhere?
There are few callers of nb_inplace_power at all (AFAICT, only PyNumber_InPlacePower); in turn, PyNumber_InPlacePower is called with the implicit Py_None always: - ceval.c, for INPLACE_POWER (which is binary) - operator.ipow (which is also binary) - class.c, from bin_inplace_power, which in turn is called from instance_ipow if the instance's 3rd argument to nb_inplace_power is Py_None (if there is a non-None third argument, instance_ipow invokes __ipow__ with three arguments if __ipow__ is defined, else it invokes __pow__ with three arguments) The only case I could find where a third argument is non-None is when the builtin pow() is invoked, which then invokes nb_power (but not nb_inplace_power) with three arguments. Regards, Martin
On 2/9/07, "Martin v. Löwis" <Martin.vonLoewis@hpi.uni-potsdam.de> wrote:
Brett Cannon schrieb:
Seems reasonable to me. Is the argument of None passed in automatically somewhere?
There are few callers of nb_inplace_power at all (AFAICT, only PyNumber_InPlacePower); in turn, PyNumber_InPlacePower is called with the implicit Py_None always: - ceval.c, for INPLACE_POWER (which is binary) - operator.ipow (which is also binary) - class.c, from bin_inplace_power, which in turn is called from instance_ipow if the instance's 3rd argument to nb_inplace_power is Py_None (if there is a non-None third argument, instance_ipow invokes __ipow__ with three arguments if __ipow__ is defined, else it invokes __pow__ with three arguments)
The only case I could find where a third argument is non-None is when the builtin pow() is invoked, which then invokes nb_power (but not nb_inplace_power) with three arguments.
Well then explicitly ignoring the object makes sense to me. While I am personally fine with raising the exception, erring on the side of caution as Raymond is suggesting wouldn't hurt either. -Brett
Martin v. Löwis wrote:
It is the implementation of
foo **= bar
(and that's its only use), so it ought to be binary.
Maybe it's so that a type can plug the same implementation into both nb_pow and nb_inplace_pow. Although the same effect could be achieved by just leaving nb_inplace_pow null, so I suppose that's not necessary. Might we want to add an in-place version of the 3-arg pow() function one day? If so, leaving the third argument there could be useful. -- Greg
Greg Ewing schrieb:
Might we want to add an in-place version of the 3-arg pow() function one day? If so, leaving the third argument there could be useful.
What could the syntax for that be? Instead of writing x = pow(x, n, 10) would you write x pow n = 10 ? or perhaps x ** n = 10 or x * n *= 10 Also, it would break existing __ipow__ implementations that only receive two arguments (unless there would be another __ method introduced). Regards, Martin
Martin v. Löwis wrote:
Greg Ewing schrieb:
Might we want to add an in-place version of the 3-arg pow() function one day?
What could the syntax for that be?
It wouldn't be a syntax, just a function, e.g. ipow(x, n, 10)
Also, it would break existing __ipow__ implementations that only receive two arguments
You could consider them broken already, since the signature clearly implies that there could be a third argument. The fact that it's not currently used is no excuse. :-) -- Greg
The same way += et al. are in-place: it would ask 'x' to modify itself, if it can. If not, no harm done. (It would be called as 'x = ipow(x, n, 10)' of course, just like 'x += n' is really 'x = x.__iadd__(n)') On 2/10/07, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Greg Ewing schrieb:
What could the syntax for that be?
It wouldn't be a syntax, just a function, e.g.
ipow(x, n, 10)
In what way would that be inplace? A function cannot rebind the variables it gets as parameters.
Regards, Martin _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/thomas%40python.org
-- Thomas Wouters <thomas@python.org> Hi! I'm a .signature virus! copy me into your .signature file to help me spread!
Thomas Wouters schrieb:
The same way += et al. are in-place: it would ask 'x' to modify itself, if it can. If not, no harm done. (It would be called as 'x = ipow(x, n, 10)' of course, just like 'x += n' is really 'x = x.__iadd__(n)')
I think this would violate the policy that a mutating function shouldn't give the object being modified as the result - just as list.reverse doesn't return the list, in addition to reversing it in-place. Regards, Martin
Sure, and I don't know if anyone will ever want ipow() -- but I've never seen real code use the three-argument pow() either. The fact is that all the in-place modifying hooks return the result (which may or may not be self, and may or may not be mutated) so an in-place three-argument pow() would have to do the same. I would prefer keeping the similarity between __ipow__ and __pow__, although I don't care if that means keeping the always-unused third argument to __ipow__ (which isn't really in the way, after all) or adding a new hook for the three-argument pow(). On 2/14/07, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Thomas Wouters schrieb:
The same way += et al. are in-place: it would ask 'x' to modify itself, if it can. If not, no harm done. (It would be called as 'x = ipow(x, n, 10)' of course, just like 'x += n' is really 'x = x.__iadd__(n)')
I think this would violate the policy that a mutating function shouldn't give the object being modified as the result - just as list.reverse doesn't return the list, in addition to reversing it in-place.
Regards, Martin
-- Thomas Wouters <thomas@python.org> Hi! I'm a .signature virus! copy me into your .signature file to help me spread!
Thomas Wouters schrieb:
Sure, and I don't know if anyone will ever want ipow() -- but I've never seen real code use the three-argument pow() either. The fact is that all the in-place modifying hooks return the result (which may or may not be self, and may or may not be mutated) so an in-place three-argument pow() would have to do the same. I would prefer keeping the similarity between __ipow__ and __pow__, although I don't care if that means keeping the always-unused third argument to __ipow__ (which isn't really in the way, after all) or adding a new hook for the three-argument pow().
It's in the way in the sense that slot_nb_inplace_power discards its third argument silently. That shouldn't happen, so I still prefer removing it throughout. Would you volunteer adding to fix that? Regards, Martin
Martin v. Löwis wrote:
I think this would violate the policy that a mutating function shouldn't give the object being modified as the result
Well, it's a necessary violation, given the way the inplace methods work. And it doesn't *necessarily* return the same value, it might return a new object. So the return value conveys useful information, unlike with list.sort() et al. -- Greg
participants (5)
-
"Martin v. Löwis" -
"Martin v. Löwis" -
Brett Cannon -
Greg Ewing -
Thomas Wouters