Restify PEP 303 (#142)

This commit is contained in:
Mariatta 2016-11-28 08:09:28 -08:00 committed by Berker Peksag
parent 61dcc6ef2d
commit 84de20b55d
1 changed files with 150 additions and 132 deletions

View File

@ -5,192 +5,210 @@ Last-Modified: $Date$
Author: Thomas Bellman <bellman+pep-divmod@lysator.liu.se> Author: Thomas Bellman <bellman+pep-divmod@lysator.liu.se>
Status: Rejected Status: Rejected
Type: Standards Track Type: Standards Track
Content-Type: text/plain Content-Type: text/x-rst
Created: 31-Dec-2002 Created: 31-Dec-2002
Python-Version: 2.3 Python-Version: 2.3
Post-History: Post-History:
Abstract 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.
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 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]_.
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.
Specification Specification
=============
The built-in divmod() function would be changed to accept multiple The built-in ``divmod()`` function would be changed to accept multiple
divisors, changing its signature from divmod(dividend, divisor) to divisors, changing its signature from ``divmod(dividend, divisor)`` to
divmod(dividend, *divisors). The dividend is divided by the last ``divmod(dividend, divisors)``. The dividend is divided by the last
divisor, giving a quotient and a remainder. The quotient is then divisor, giving a quotient and a remainder. The quotient is then
divided by the second to last divisor, giving a new quotient and divided by the second to last divisor, giving a new quotient and
remainder. This is repeated until all divisors have been used, remainder. This is repeated until all divisors have been used,
and divmod() then returns a tuple consisting of the quotient from and ``divmod()`` then returns a tuple consisting of the quotient from
the last step, and the remainders from all the steps. the last step, and the remainders from all the steps.
A Python implementation of the new divmod() behaviour could look A Python implementation of the new ``divmod()`` behaviour could look
like: like::
def divmod(dividend, *divisors): def divmod(dividend, *divisors):
modulos = () modulos = ()
q = dividend q = dividend
while divisors: while divisors:
q,r = q.__divmod__(divisors[-1]) q, r = q.__divmod__(divisors[-1])
modulos = (r,) + modulos modulos = (r,) + modulos
divisors = divisors[:-1] divisors = divisors[:-1]
return (q,) + modulos return (q,) + modulos
Motivation Motivation
==========
Occasionally one wants to perform a chain of divmod() operations, Occasionally one wants to perform a chain of ``divmod()`` operations,
calling divmod() on the quotient from the previous step, with calling ``divmod()`` on the quotient from the previous step, with
varying divisors. The most common case is probably converting a varying divisors. The most common case is probably converting a
number of seconds into weeks, days, hours, minutes and seconds. number of seconds into weeks, days, hours, minutes and seconds.
This would today be written as: This would today be written as::
def secs_to_wdhms(seconds): def secs_to_wdhms(seconds):
m,s = divmod(seconds, 60) m, s = divmod(seconds, 60)
h,m = divmod(m, 60) h, m = divmod(m, 60)
d,h = divmod(h, 24) d, h = divmod(h, 24)
w,d = divmod(d, 7) w, d = divmod(d, 7)
return (w,d,h,m,s) return (w, d, h, m, s)
This is tedious and easy to get wrong each time you need it. This is tedious and easy to get wrong each time you need it.
If instead the divmod() built-in is changed according the proposal, If instead the ``divmod()`` built-in is changed according the proposal,
the code for converting seconds to weeks, days, hours, minutes and the code for converting seconds to weeks, days, hours, minutes and
seconds then become seconds then become::
def secs_to_wdhms(seconds): def secs_to_wdhms(seconds):
w,d,h,m,s = divmod(seconds, 7, 24, 60, 60) w, d, h, m, s = divmod(seconds, 7, 24, 60, 60)
return (w,d,h,m,s) return (w, d, h, m, s)
which is easier to type, easier to type correctly, and easier to which is easier to type, easier to type correctly, and easier to
read. read.
Other applications are: Other applications are:
- Astronomical angles (declination is measured in degrees, minutes - Astronomical angles (declination is measured in degrees, minutes
and seconds, right ascension is measured in hours, minutes and and seconds, right ascension is measured in hours, minutes and
seconds). seconds).
- Old British currency (1 pound = 20 shilling, 1 shilling = 12 pence) - Old British currency (1 pound = 20 shilling, 1 shilling = 12 pence)
- Anglo-Saxon length units: 1 mile = 1760 yards, 1 yard = 3 feet, - Anglo-Saxon length units: 1 mile = 1760 yards, 1 yard = 3 feet,
1 foot = 12 inches. 1 foot = 12 inches.
- Anglo-Saxon weight units: 1 long ton = 160 stone, 1 stone = 14 - Anglo-Saxon weight units: 1 long ton = 160 stone, 1 stone = 14
pounds, 1 pound = 16 ounce, 1 ounce = 16 dram pounds, 1 pound = 16 ounce, 1 ounce = 16 dram
- British volumes: 1 gallon = 4 quart, 1 quart = 2 pint, 1 pint - British volumes: 1 gallon = 4 quart, 1 quart = 2 pint, 1 pint
= 20 fluid ounces = 20 fluid ounces
Rationale Rationale
=========
The idea comes from APL, which has an operator that does this. (I 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 don't remember what the operator looks like, and it would probably
be impossible to render in ASCII anyway.) be impossible to render in ASCII anyway.)
The APL operator takes a list as its second operand, while this The APL operator takes a list as its second operand, while this
PEP proposes that each divisor should be a separate argument to PEP proposes that each divisor should be a separate argument to
the divmod() function. This is mainly because it is expected that the ``divmod()`` function. This is mainly because it is expected that
the most common uses will have the divisors as constants right in 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 the call (as the 7, 24, 60, 60 above), and adding a set of
parentheses or brackets would just clutter the call. parentheses or brackets would just clutter the call.
Requiring an explicit sequence as the second argument to divmod() Requiring an explicit sequence as the second argument to ``divmod()``
would seriously break backwards compatibility. Making divmod() would seriously break backwards compatibility. Making ``divmod()``
check its second argument for being a sequence is deemed to be too 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 ugly to contemplate. And in the case where one *does* have a
sequence that is computed other-where, it is easy enough to write sequence that is computed other-where, it is easy enough to write
divmod(x, *divs) instead. ``divmod(x, *divs)`` instead.
Requiring at least one divisor, i.e rejecting divmod(x), has been 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 considered, but no good reason to do so has come to mind, and is
thus allowed in the name of generality. thus allowed in the name of generality.
Calling divmod() with no divisors should still return a tuple (of Calling ``divmod()`` with no divisors should still return a tuple (of
one element). Code that calls divmod() with a varying number of one element). Code that calls ``divmod()`` with a varying number of
divisors, and thus gets a return value with an "unknown" number of divisors, and thus gets a return value with an "unknown" number of
elements, would otherwise have to special case that case. Code elements, would otherwise have to special case that case. Code
that *knows* it is calling divmod() with no divisors is considered that *knows* it is calling ``divmod()`` with no divisors is considered
to be too silly to warrant a special case. to be too silly to warrant a special case.
Processing the divisors in the other direction, i.e dividing with Processing the divisors in the other direction, i.e dividing with
the first divisor first, instead of dividing with the last divisor the first divisor first, instead of dividing with the last divisor
first, has been considered. However, the result comes with the first, has been considered. However, the result comes with the
most significant part first and the least significant part last most significant part first and the least significant part last
(think of the chained divmod as a way of splitting a number into (think of the chained divmod as a way of splitting a number into
"digits", with varying weights), and it is reasonable to specify "digits", with varying weights), and it is reasonable to specify
the divisors (weights) in the same order as the result. the divisors (weights) in the same order as the result.
The inverse operation: The inverse operation::
def inverse_divmod(seq, *factors): def inverse_divmod(seq, *factors):
product = seq[0] product = seq[0]
for x,y in zip(factors, seq[1:]): for x, y in zip(factors, seq[1:]):
product = product * x + y product = product * x + y
return product return product
could also be useful. However, writing could also be useful. However, writing::
seconds = (((((w * 7) + d) * 24 + h) * 60 + m) * 60 + s) seconds = (((((w * 7) + d) * 24 + h) * 60 + m) * 60 + s)
is less cumbersome both to write and to read than the chained is less cumbersome both to write and to read than the chained
divmods. It is therefore deemed to be less important, and its divmods. It is therefore deemed to be less important, and its
introduction can be deferred to its own PEP. Also, such a introduction can be deferred to its own PEP. Also, such a
function needs a good name, and the PEP author has not managed to function needs a good name, and the PEP author has not managed to
come up with one yet. come up with one yet.
Calling divmod("spam") does not raise an error, despite strings Calling ``divmod("spam")`` does not raise an error, despite strings
supporting neither division nor modulo. However, unless we know supporting neither division nor modulo. However, unless we know
the other object too, we can't determine whether divmod() would the other object too, we can't determine whether ``divmod()`` would
work or not, and thus it seems silly to forbid it. work or not, and thus it seems silly to forbid it.
Backwards Compatibility Backwards Compatibility
=======================
Any module that replaces the divmod() function in the __builtin__ Any module that replaces the ``divmod()`` function in the ``__builtin__``
module, may cause other modules using the new syntax to break. It module, may cause other modules using the new syntax to break. It
is expected that this is very uncommon. is expected that this is very uncommon.
Code that expects a TypeError exception when calling divmod() with Code that expects a ``TypeError`` exception when calling ``divmod()`` with
anything but two arguments will break. This is also expected to anything but two arguments will break. This is also expected to
be very uncommon. be very uncommon.
No other issues regarding backwards compatibility are known. No other issues regarding backwards compatibility are known.
Reference Implementation Reference Implementation
========================
Not finished yet, but it seems a rather straightforward Not finished yet, but it seems a rather straightforward
new implementation of the function builtin_divmod() in new implementation of the function ``builtin_divmod()`` in
Python/bltinmodule.c ``Python/bltinmodule.c``.
References
==========
.. [1] Raymond Hettinger, "Propose rejection of PEP 303 -- Extend divmod() for
Multiple Divisors" http://mail.python.org/pipermail/python-dev/2003-January/032492.html
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
sentence-end-double-space: t indent-tabs-mode: nil
fill-column: 70 sentence-end-double-space: t
End: fill-column: 70
End: