Permalink
Cannot retrieve contributors at this time
214 lines (162 sloc)
7.29 KB
| PEP: 303 | |
| Title: Extend divmod() for Multiple Divisors | |
| Version: $Revision$ | |
| Last-Modified: $Date$ | |
| Author: Thomas Bellman <bellman+pep-divmod@lysator.liu.se> | |
| Status: Rejected | |
| Type: Standards Track | |
| Content-Type: text/x-rst | |
| Created: 31-Dec-2002 | |
| Python-Version: 2.3 | |
| Post-History: | |
| Abstract | |
| ======== | |
| This PEP describes an extension to the built-in ``divmod()`` function, | |
| allowing it to take multiple divisors, chaining several calls to | |
| ``divmod()`` into one. | |
| Pronouncement | |
| ============= | |
| This PEP is rejected. Most uses for chained ``divmod()`` involve a | |
| constant modulus (in radix conversions for example) and are more | |
| properly coded as a loop. The example of splitting seconds | |
| into days/hours/minutes/seconds does not generalize to months | |
| and years; rather, the whole use case is handled more flexibly and | |
| robustly by date and time modules. The other use cases mentioned | |
| in the PEP are somewhat rare in real code. The proposal is also | |
| problematic in terms of clarity and obviousness. In the examples, | |
| it is not immediately clear that the argument order is correct or | |
| that the target tuple is of the right length. Users from other | |
| languages are more likely to understand the standard two argument | |
| form without having to re-read the documentation. See python-dev | |
| discussion on 17 June 2005 [1]_. | |
| Specification | |
| ============= | |
| 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 | |
| like:: | |
| def divmod(dividend, *divisors): | |
| modulos = () | |
| q = dividend | |
| while divisors: | |
| q, r = q.__divmod__(divisors[-1]) | |
| modulos = (r,) + modulos | |
| divisors = divisors[:-1] | |
| return (q,) + modulos | |
| Motivation | |
| ========== | |
| 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 | |
| read. | |
| Other applications are: | |
| - Astronomical angles (declination is measured in degrees, minutes | |
| and seconds, right ascension is measured in hours, minutes and | |
| seconds). | |
| - 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. | |
| Rationale | |
| ========= | |
| 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 | |
| ``Python/bltinmodule.c``. | |
| References | |
| ========== | |
| .. [1] Raymond Hettinger, "Propose rejection of PEP 303 -- Extend divmod() for | |
| Multiple Divisors" https://mail.python.org/pipermail/python-dev/2005-June/054283.html | |
| Copyright | |
| ========= | |
| This document has been placed in the public domain. | |
| .. | |
| Local Variables: | |
| mode: indented-text | |
| indent-tabs-mode: nil | |
| sentence-end-double-space: t | |
| fill-column: 70 | |
| End: |