in-place exponentiation incongruities

G

Giacomo Alzetta

I've noticed some incongruities regarding in-place exponentiation.

On the C side nb_inplace_power is a ternary function, like nb_power (see here: http://docs.python.org/c-api/typeobj.html?highlight=numbermethods#PyNumberMethods).

Obviously you can't pass the third argument using the usual in-place syntax"**=".
Nevertheless I'd expect to be able to provide the third argument using operator.ipow. But the operator module accept only the two parameter variant.

The Number Protocol specify that the ipow operation ""is the equivalent of the Python statement o1 **= o2 when o3 is Py_None, or an in-place variantof pow(o1, o2, o3) otherwise.""

Since "operator" claims to be contain a "function port" of the operators, I'd expect it to implement ipow with three arguments.
I don't see any problem in adding the third argument to it(I mean, sure right now any code that calls ipow(a,b,c) [if exists] is broken, because it will just raise a TypeError, thus adding the argument will not break any code, and would provide more functionality.

Also, I don't think there are many objects in the build-ins or standardlib which implement an in-place exponentiation, so this means there wont be much code to change.

So my question is: why are there this incongruities?
Is there any chance to see this fixed? (in the operator module, or changingthe documentation)

By the way: I'm asking this because I'm implementing a C-extension and I'd like to implement both pow and ipow. And since it's about polynomials on (Z/nZ)[x]/x^r-1, using the third argument always makes sense.
 
S

Steven D'Aprano

I've noticed some incongruities regarding in-place exponentiation.

On the C side nb_inplace_power is a ternary function, like nb_power (see
here:
http://docs.python.org/c-api/typeobj.html? highlight=numbermethods#PyNumberMethods).

Obviously you can't pass the third argument using the usual in-place
syntax "**=". Nevertheless I'd expect to be able to provide the third
argument using operator.ipow. But the operator module accept only the
two parameter variant.

Why? The operator module implements the ** operator, not the pow()
function. If you want the pow() function, you can just use it directly,
no need to use operator.pow or operator.ipow.

Since ** is a binary operator, it can only accept two arguments.

The Number Protocol specify that the ipow operation ""is the equivalent
of the Python statement o1 **= o2 when o3 is Py_None, or an in-place
variant of pow(o1, o2, o3) otherwise.""

Where is that from?
 
G

Giacomo Alzetta

Il giorno domenica 12 agosto 2012 06:28:10 UTC+2, Steven D'Aprano ha scritto:
Why? The operator module implements the ** operator, not the pow()

function. If you want the pow() function, you can just use it directly,

no need to use operator.pow or operator.ipow.



Since ** is a binary operator, it can only accept two arguments.










Where is that from?

From The Number Protocol(http://docs.python.org/c-api/number.html).
The full text is:

PyObject* PyNumber_InPlacePower(PyObject *o1, PyObject *o2, PyObject *o3)
Return value: New reference.

**See the built-in function pow().** Returns NULL on failure. The operation is done in-place when o1 supports it. This is the equivalent of the Python statement o1 **= o2 when o3 is Py_None, or an in-place variant of pow(o1, o2, o3) otherwise. If o3 is to be ignored, pass Py_None in its place (passing NULL for o3 would cause an illegal memory access).

The first thing that this text does is referring to the **function** pow, which takes three arguments. And since the documentation of the operator module states that "The operator module exports a set of efficient functions corresponding to the intrinsic operators of Python.", I'd expect the ipow tohave three arguments, the third being optional.


With normal exponentiation you have ** referring to the 2-argument variant,and "pow" providing the ability to use the third argument.
At the moment in-place exponentiation you have "**=" referring to the 2-argument variant(and this is consistent), while operator.ipow also referringto it. So providing an ipow with the third argument would just increase consistency in the language, and provide a feature that at the moment is not present. (well if the designers of python care really much about consistency they'd probably add an "ipow" built-in function, so that you don't have to import it from "operator").

I understand that it's not a feature often used, but I can't see why not allowing it.
At the moment you can do that from the C side, because you can call PyNumber_InPlacePower directly, but from the python side you have no way to do that, except for writing a C-extension that wraps the PyNumber_InPlacePower function.
 
S

Steven D'Aprano

From The Number Protocol(http://docs.python.org/c-api/number.html). The
full text is:

PyObject* PyNumber_InPlacePower(PyObject *o1, PyObject *o2, PyObject
*o3)
Return value: New reference.

**See the built-in function pow().** Returns NULL on failure. The
operation is done in-place when o1 supports it. This is the
equivalent of the Python statement o1 **= o2 when o3 is Py_None, or
an in-place variant of pow(o1, o2, o3) otherwise. If o3 is to be
ignored, pass Py_None in its place (passing NULL for o3 would cause
an illegal memory access).

The first thing that this text does is referring to the **function**
pow, which takes three arguments. And since the documentation of the
operator module states that "The operator module exports a set of
efficient functions corresponding to the intrinsic operators of
Python.", I'd expect the ipow to have three arguments, the third being
optional.

Why? There is no three-argument operator. There is a three-argument
function, pow, but you don't need the operator module for that, it is
built-in. There is no in-place three-argument operator, and no in-place
three-argument function in the operator module.

Arguing from "consistency" is not going to get you very far, since it is
already consistent:

In-place binary operator: **=
In-place binary function: operator.ipow

In-place three-argument operator: none
In-place three-argument function: none

If you decide to make a feature-request, you need to argue from
usefulness, not consistency.

http://bugs.python.org

Remember that the Python 2.x branch is now in feature-freeze, so new
features only apply to Python 3.x.

With normal exponentiation you have ** referring to the 2-argument
variant, and "pow" providing the ability to use the third argument.
Correct.


At the moment in-place exponentiation you have "**=" referring to the
2-argument variant(and this is consistent), while operator.ipow also
referring to it.

Correct. Both **= and ipow match the ** operator, which only takes two
arguments.

So providing an ipow with the third argument would just
increase consistency in the language,

Consistency with something other than **= would be inconsistency.


and provide a feature that at the
moment is not present. (well if the designers of python care really much
about consistency they'd probably add an "ipow" built-in function, so
that you don't have to import it from "operator").

Not everything needs to be a built-in function.
 
G

Giacomo Alzetta

Il giorno domenica 12 agosto 2012 13:03:08 UTC+2, Steven D'Aprano ha scritto:
Why? There is no three-argument operator. There is a three-argument

function, pow, but you don't need the operator module for that, it is

built-in. There is no in-place three-argument operator, and no in-place

three-argument function in the operator module.



Arguing from "consistency" is not going to get you very far, since it is

already consistent:



In-place binary operator: **=

In-place binary function: operator.ipow



In-place three-argument operator: none

In-place three-argument function: none



If you decide to make a feature-request, you need to argue from

usefulness, not consistency.



http://bugs.python.org



Remember that the Python 2.x branch is now in feature-freeze, so new

features only apply to Python 3.x.











Correct. Both **= and ipow match the ** operator, which only takes two

arguments.









Consistency with something other than **= would be inconsistency.













Not everything needs to be a built-in function.

Probably I've mixed things up.

What I mean is: when you implement a new type as a C extension you have to provide special methods through the NumberMethods struct. In this struct both the power and in-place power operations have three arguments.
Now, suppose I implement the three argument variant of the in-place power in a class. No user would be able to call my C function with a non-None third argument, while he would be able to call the normal version with the third argument.

This is what I find inconsistent. either provide a way to call also the in-place exponentiation with three arguments, or define it as a binary function.
Having it as a ternary function, but only from the C-side is quite strange.
 
T

Terry Reedy

What I mean is: when you implement a new type as a C extension you
have to provide special methods through the NumberMethods struct. In
this struct both the power and in-place power operations have three
arguments.

I am not really sure why the latter is true. Probably 'consistency' at
the special method level, with __pow__. As Steven points out, it is not
needed to implement normal Python code.

This is one area of Python where the design is a bit messy. I believe
the very existence of __ipow__ is a matter of consistency than of known
use cases. Guido was not sure which __ixxx__ would ever be needed (for
mutable objects), so he just put them all in.

At the Python level, both __pow__ and __ipow__ are also documented in
the manual as having an optional, third, modulo parameter. __rpow__ is
defined as binary, but that is a mistake
1


Now, suppose I implement the three argument variant of the
in-place power in a class.

Are you actually planning to do this, or is this purely theoretical?
No user would be able to call my C
function with a non-None third argument,

Not true. Whether the function is coded in Python or C
cls.__ipow__(base, exp, mod) # or
base.__ipow__(exp, mod)
while he would be able to
call the normal version with the third argument.

That can also be done directly1
 
G

Giacomo Alzetta

Il giorno domenica 12 agosto 2012 23:53:46 UTC+2, Terry Reedy ha scritto:
Are you actually planning to do this, or is this purely theoretical?

Yes, I do plan to implement ipow.
Not true. Whether the function is coded in Python or C

cls.__ipow__(base, exp, mod) # or

base.__ipow__(exp, mod)

Yes, that's true. But I find that calling a special-method in that way is *really* ugly. I'd think that the pow built-in function was probably inserted to avoid writing base.__pow__(exp, mod).
 
G

Giacomo Alzetta

Il giorno domenica 12 agosto 2012 23:53:46 UTC+2, Terry Reedy ha scritto:
Are you actually planning to do this, or is this purely theoretical?

Yes, I do plan to implement ipow.
Not true. Whether the function is coded in Python or C

cls.__ipow__(base, exp, mod) # or

base.__ipow__(exp, mod)

Yes, that's true. But I find that calling a special-method in that way is *really* ugly. I'd think that the pow built-in function was probably inserted to avoid writing base.__pow__(exp, mod).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top