in-place exponentiation incongruities

Discussion in 'Python' started by Giacomo Alzetta, Aug 11, 2012.

  1. 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.
     
    Giacomo Alzetta, Aug 11, 2012
    #1
    1. Advertising

  2. 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
     
    Steven D'Aprano, Aug 12, 2012
    #2
    1. Advertising

  3. 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 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.
     
    Giacomo Alzetta, Aug 12, 2012
    #3
  4. 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
     
    Steven D'Aprano, Aug 12, 2012
    #4
  5. 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.
     
    Giacomo Alzetta, Aug 12, 2012
    #5
  6. Giacomo Alzetta

    Terry Reedy Guest

    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
     
    Terry Reedy, Aug 12, 2012
    #6
  7. 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).
     
    Giacomo Alzetta, Aug 14, 2012
    #7
  8. 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).
     
    Giacomo Alzetta, Aug 14, 2012
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steven T. Hatton

    An exponentiation function for int?

    Steven T. Hatton, Oct 13, 2004, in forum: C++
    Replies:
    14
    Views:
    748
    JXStern
    Oct 16, 2004
  2. Jeff Davis

    strange exponentiation performance

    Jeff Davis, Nov 23, 2003, in forum: Python
    Replies:
    0
    Views:
    342
    Jeff Davis
    Nov 23, 2003
  3. Tim Peters
    Replies:
    1
    Views:
    437
    Jeff Davis
    Nov 24, 2003
  4. elventear

    Decimal and Exponentiation

    elventear, May 19, 2006, in forum: Python
    Replies:
    7
    Views:
    642
    Tim Peters
    May 20, 2006
  5. exponentiation operator (lack of)

    , Dec 22, 2005, in forum: C Programming
    Replies:
    67
    Views:
    1,518
    Dave Thompson
    Jan 4, 2006
Loading...

Share This Page