[Python-Dev] PEP 303: Extend divmod() for Multiple Divisors

Thomas Bellman bellman+pep-divmod@lysator.liu.se
Tue, 31 Dec 2002 18:38:37 +0100 (MET)

I humbly submit this PEP for your dissection.

As I am not subscribed to python-dev, please make sure that your
comments are CC:ed to <bellman+pep-divmod@lysator.liu.se>, so I
can see them.

I have also posted this to comp.lang.python.

I have written an implementation also, but I need to check it
some more to see if I've got the reference counting correct
before I dare post it. :-)

PEP: 303
Title: Extend divmod() for Multiple Divisors
Version: $Revision: 1.2 $
Last-Modified: $Date: 2002/12/31 16:02:49 $
Author: Thomas Bellman <bellman+pep-divmod@lysator.liu.se>
Status: Draft
Type: Standards Track
Content-Type: text/plain
Created: 31-Dec-2002
Python-Version: 2.3


    This PEP describes an extension to the built-in divmod() function,
    allowing it to take multiple divisors, chaining several calls to
    divmod() into one.


    The built-in divmod() function would be changed to accept multiple
    divisors, changing its signature from divmod(dividend, divisor) to
    divmod(dividend, *divisors).  The dividend is divided by the last
    divisor, giving a quotient and a remainder.  The quotient is then
    divided by the second to last divisor, giving a new quotient and
    remainder.  This is repeated until all divisors have been used,
    and divmod() then returns a tuple consisting of the quotient from
    the last step, and the remainders from all the steps.

    A Python implementation of the new divmod() behaviour could look

        def divmod(dividend, *divisors):
            modulos = ()
            q = dividend
            while divisors:
                q,r = q.__divmod__(divisors[-1])
                modulos = (r,) + modulos
                divisors = divisors[:-1]
            return (q,) + modulos


    Occasionally one wants to perform a chain of divmod() operations,
    calling divmod() on the quotient from the previous step, with
    varying divisors.  The most common case is probably converting a
    number of seconds into weeks, days, hours, minutes and seconds.
    This would today be written as:

        def secs_to_wdhms(seconds):
            m,s = divmod(seconds, 60)
            h,m = divmod(m, 60)
            d,h = divmod(h, 24)
            w,d = divmod(d, 7)
            return (w,d,h,m,s)

    This is tedious and easy to get wrong each time you need it.

    If instead the divmod() built-in is changed according the proposal,
    the code for converting seconds to weeks, days, hours, minutes and
    seconds then become

        def secs_to_wdhms(seconds):
            w,d,h,m,s = divmod(seconds, 7, 24, 60, 60)
            return (w,d,h,m,s)

    which is easier to type, easier to type correctly, and easier to

    Other applications are:

    - Astronomical angles (declination is measured in degrees, minutes
      and seconds, right ascension is measured in hours, minutes and
    - Old British currency (1 pound = 20 shilling, 1 shilling = 12 pence)
    - Anglo-Saxon length units: 1 mile = 1760 yards, 1 yard = 3 feet,
      1 foot = 12 inches.
    - Anglo-Saxon weight units: 1 long ton = 160 stone, 1 stone = 14
      pounds, 1 pound = 16 ounce, 1 ounce = 16 dram
    - British volumes: 1 gallon = 4 quart, 1 quart = 2 pint, 1 pint
      = 20 fluid ounces


    The idea comes from APL, which has an operator that does this.  (I
    don't remember what the operator looks like, and it would probably
    be impossible to render in ASCII anyway.)

    The APL operator takes a list as its second operand, while this
    PEP proposes that each divisor should be a separate argument to
    the divmod() function.  This is mainly because it is expected that
    the most common uses will have the divisors as constants right in
    the call (as the 7, 24, 60, 60 above), and adding a set of
    parentheses or brackets would just clutter the call.

    Requiring an explicit sequence as the second argument to divmod()
    would seriously break backwards compatibility.  Making divmod()
    check its second argument for being a sequence is deemed to be too
    ugly to contemplate.  And in the case where one *does* have a
    sequence that is computed other-where, it is easy enough to write
    divmod(x, *divs) instead.

    Requiring at least one divisor, i.e rejecting divmod(x), has been
    considered, but no good reason to do so has come to mind, and is
    thus allowed in the name of generality.

    Calling divmod() with no divisors should still return a tuple (of
    one element).  Code that calls divmod() with a varying number of
    divisors, and thus gets a return value with an "unknown" number of
    elements, would otherwise have to special case that case.  Code
    that *knows* it is calling divmod() with no divisors is considered
    to be too silly to warrant a special case.

    Processing the divisors in the other direction, i.e dividing with
    the first divisor first, instead of dividing with the last divisor
    first, has been considered.  However, the result comes with the
    most significant part first and the least significant part last
    (think of the chained divmod as a way of splitting a number into
    "digits", with varying weights), and it is reasonable to specify
    the divisors (weights) in the same order as the result.

    The inverse operation:

        def inverse_divmod(seq, *factors):
            product = seq[0]
            for x,y in zip(factors, seq[1:]):
                product = product * x + y
            return product

    could also be useful.  However, writing

        seconds = (((((w * 7) + d) * 24 + h) * 60 + m) * 60 + s)

    is less cumbersome both to write and to read than the chained
    divmods.  It is therefore deemed to be less important, and its
    introduction can be deferred to its own PEP.  Also, such a
    function needs a good name, and the PEP author has not managed to
    come up with one yet.

    Calling divmod("spam") does not raise an error, despite strings
    supporting neither division nor modulo.  However, unless we know
    the other object too, we can't determine whether divmod() would
    work or not, and thus it seems silly to forbid it.

Backwards Compatibility

    Any module that replaces the divmod() function in the __builtin__
    module, may cause other modules using the new syntax to break.  It
    is expected that this is very uncommon.

    Code that expects a TypeError exception when calling divmod() with
    anything but two arguments will break.  This is also expected to
    be very uncommon.

    No other issues regarding backwards compatibility are known.

Reference Implementation

    Not finished yet, but it seems a rather straightforward
    new implementation of the function builtin_divmod() in


    This document has been placed in the public domain.

Thomas Bellman,   Lysator Computer Club,   Linköping University,  Sweden
"Adde parvum parvo magnus acervus erit"       ! bellman @ lysator.liu.se
          (From The Mythical Man-Month)       ! Make Love -- Nicht Wahr!