mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
Close #20481: Disallow mixed type input in statistics
The most appropriate coercion rules are not yet clear, so simply disallowing mixed type input for 3.4. (Committed on Steven's behalf)
This commit is contained in:
parent
57e4127781
commit
73afe2a972
4 changed files with 66 additions and 76 deletions
|
@ -144,19 +144,31 @@ def _sum(data, start=0):
|
|||
>>> _sum(data)
|
||||
Decimal('0.6963')
|
||||
|
||||
Mixed types are currently treated as an error, except that int is
|
||||
allowed.
|
||||
"""
|
||||
# We fail as soon as we reach a value that is not an int or the type of
|
||||
# the first value which is not an int. E.g. _sum([int, int, float, int])
|
||||
# is okay, but sum([int, int, float, Fraction]) is not.
|
||||
allowed_types = set([int, type(start)])
|
||||
n, d = _exact_ratio(start)
|
||||
T = type(start)
|
||||
partials = {d: n} # map {denominator: sum of numerators}
|
||||
# Micro-optimizations.
|
||||
coerce_types = _coerce_types
|
||||
exact_ratio = _exact_ratio
|
||||
partials_get = partials.get
|
||||
# Add numerators for each denominator, and track the "current" type.
|
||||
# Add numerators for each denominator.
|
||||
for x in data:
|
||||
T = _coerce_types(T, type(x))
|
||||
_check_type(type(x), allowed_types)
|
||||
n, d = exact_ratio(x)
|
||||
partials[d] = partials_get(d, 0) + n
|
||||
# Find the expected result type. If allowed_types has only one item, it
|
||||
# will be int; if it has two, use the one which isn't int.
|
||||
assert len(allowed_types) in (1, 2)
|
||||
if len(allowed_types) == 1:
|
||||
assert allowed_types.pop() is int
|
||||
T = int
|
||||
else:
|
||||
T = (allowed_types - set([int])).pop()
|
||||
if None in partials:
|
||||
assert issubclass(T, (float, Decimal))
|
||||
assert not math.isfinite(partials[None])
|
||||
|
@ -172,6 +184,15 @@ def _sum(data, start=0):
|
|||
return T(total)
|
||||
|
||||
|
||||
def _check_type(T, allowed):
|
||||
if T not in allowed:
|
||||
if len(allowed) == 1:
|
||||
allowed.add(T)
|
||||
else:
|
||||
types = ', '.join([t.__name__ for t in allowed] + [T.__name__])
|
||||
raise TypeError("unsupported mixed types: %s" % types)
|
||||
|
||||
|
||||
def _exact_ratio(x):
|
||||
"""Convert Real number x exactly to (numerator, denominator) pair.
|
||||
|
||||
|
@ -228,44 +249,6 @@ def _decimal_to_ratio(d):
|
|||
return (num, den)
|
||||
|
||||
|
||||
def _coerce_types(T1, T2):
|
||||
"""Coerce types T1 and T2 to a common type.
|
||||
|
||||
>>> _coerce_types(int, float)
|
||||
<class 'float'>
|
||||
|
||||
Coercion is performed according to this table, where "N/A" means
|
||||
that a TypeError exception is raised.
|
||||
|
||||
+----------+-----------+-----------+-----------+----------+
|
||||
| | int | Fraction | Decimal | float |
|
||||
+----------+-----------+-----------+-----------+----------+
|
||||
| int | int | Fraction | Decimal | float |
|
||||
| Fraction | Fraction | Fraction | N/A | float |
|
||||
| Decimal | Decimal | N/A | Decimal | float |
|
||||
| float | float | float | float | float |
|
||||
+----------+-----------+-----------+-----------+----------+
|
||||
|
||||
Subclasses trump their parent class; two subclasses of the same
|
||||
base class will be coerced to the second of the two.
|
||||
|
||||
"""
|
||||
# Get the common/fast cases out of the way first.
|
||||
if T1 is T2: return T1
|
||||
if T1 is int: return T2
|
||||
if T2 is int: return T1
|
||||
# Subclasses trump their parent class.
|
||||
if issubclass(T2, T1): return T2
|
||||
if issubclass(T1, T2): return T1
|
||||
# Floats trump everything else.
|
||||
if issubclass(T2, float): return T2
|
||||
if issubclass(T1, float): return T1
|
||||
# Subclasses of the same base class give priority to the second.
|
||||
if T1.__base__ is T2.__base__: return T2
|
||||
# Otherwise, just give up.
|
||||
raise TypeError('cannot coerce types %r and %r' % (T1, T2))
|
||||
|
||||
|
||||
def _counts(data):
|
||||
# Generate a table of sorted (value, frequency) pairs.
|
||||
table = collections.Counter(iter(data)).most_common()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue