Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #26921 > unrolled thread

in-place exponentiation incongruities

Started byGiacomo Alzetta <giacomo.alzetta@gmail.com>
First post2012-08-11 09:54 -0700
Last post2012-08-14 00:14 -0700
Articles 8 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  in-place exponentiation incongruities Giacomo Alzetta <giacomo.alzetta@gmail.com> - 2012-08-11 09:54 -0700
    Re: in-place exponentiation incongruities Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-08-12 04:28 +0000
      Re: in-place exponentiation incongruities Giacomo Alzetta <giacomo.alzetta@gmail.com> - 2012-08-12 00:14 -0700
        Re: in-place exponentiation incongruities Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-08-12 11:03 +0000
          Re: in-place exponentiation incongruities Giacomo Alzetta <giacomo.alzetta@gmail.com> - 2012-08-12 04:55 -0700
            Re: in-place exponentiation incongruities Terry Reedy <tjreedy@udel.edu> - 2012-08-12 17:53 -0400
              Re: in-place exponentiation incongruities Giacomo Alzetta <giacomo.alzetta@gmail.com> - 2012-08-14 00:14 -0700
              Re: in-place exponentiation incongruities Giacomo Alzetta <giacomo.alzetta@gmail.com> - 2012-08-14 00:14 -0700

#26921 — in-place exponentiation incongruities

FromGiacomo Alzetta <giacomo.alzetta@gmail.com>
Date2012-08-11 09:54 -0700
Subjectin-place exponentiation incongruities
Message-ID<a0d2c349-6792-4c80-8aa5-af13dbaac039@googlegroups.com>
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 variant of 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 changing the 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.

[toc] | [next] | [standalone]


#26936

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-08-12 04:28 +0000
Message-ID<502730da$0$29983$c3e8da3$5496439d@news.astraweb.com>
In reply to#26921
On Sat, 11 Aug 2012 09:54:56 -0700, Giacomo Alzetta wrote:

> 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?


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#26938

FromGiacomo Alzetta <giacomo.alzetta@gmail.com>
Date2012-08-12 00:14 -0700
Message-ID<5bc4e58c-c587-46c3-93cc-95dea97d89e8@googlegroups.com>
In reply to#26936
Il giorno domenica 12 agosto 2012 06:28:10 UTC+2, Steven D'Aprano ha scritto:
> On Sat, 11 Aug 2012 09:54:56 -0700, Giacomo Alzetta wrote:
> 
> 
> 
> > 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?
> 
> 
> 
> 
> 
> -- 
> 
> Steven

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.


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 referring to 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.

[toc] | [prev] | [next] | [standalone]


#26939

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-08-12 11:03 +0000
Message-ID<50278d6b$0$29978$c3e8da3$5496439d@news.astraweb.com>
In reply to#26938
On Sun, 12 Aug 2012 00:14:27 -0700, Giacomo Alzetta wrote:

> 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.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#26942

FromGiacomo Alzetta <giacomo.alzetta@gmail.com>
Date2012-08-12 04:55 -0700
Message-ID<3118027d-0c13-425e-a874-4593b608cf45@googlegroups.com>
In reply to#26939
Il giorno domenica 12 agosto 2012 13:03:08 UTC+2, Steven D'Aprano ha scritto:
> On Sun, 12 Aug 2012 00:14:27 -0700, Giacomo Alzetta wrote:
> 
> 
> 
> > 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.
> 
> 
> 
> 
> 
> 
> 
> -- 
> 
> Steven

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.

[toc] | [prev] | [next] | [standalone]


#26956

FromTerry Reedy <tjreedy@udel.edu>
Date2012-08-12 17:53 -0400
Message-ID<mailman.3212.1344808442.4697.python-list@python.org>
In reply to#26942
On 8/12/2012 7:55 AM, Giacomo Alzetta wrote:

> 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

 >>> int.__rpow__(3, 5, 4)
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 directly
 >>> int.__pow__(5, 3,  4)
1

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#27025

FromGiacomo Alzetta <giacomo.alzetta@gmail.com>
Date2012-08-14 00:14 -0700
Message-ID<mailman.3253.1344928503.4697.python-list@python.org>
In reply to#26956
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)
> 
> 
> 
> > while he would be able to
> 
> > call the normal version with the third argument.

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).

[toc] | [prev] | [next] | [standalone]


#27026

FromGiacomo Alzetta <giacomo.alzetta@gmail.com>
Date2012-08-14 00:14 -0700
Message-ID<75241788-1054-4b22-b8b6-930358dbbe8c@googlegroups.com>
In reply to#26956
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)
> 
> 
> 
> > while he would be able to
> 
> > call the normal version with the third argument.

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).

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web