diff options
author | Raymond Hettinger <rhettinger@users.noreply.github.com> | 2019-11-23 10:22:13 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-23 10:22:13 (GMT) |
commit | 041d8b48a2e59fa642b2c5124d78086baf74e339 (patch) | |
tree | f189a49ce0107fdeef9eeb75e9e6c63fb169e83c | |
parent | 84b1ff65609c5910b4f838adbe1ead83baae7dbf (diff) | |
download | cpython-041d8b48a2e59fa642b2c5124d78086baf74e339.zip cpython-041d8b48a2e59fa642b2c5124d78086baf74e339.tar.gz cpython-041d8b48a2e59fa642b2c5124d78086baf74e339.tar.bz2 |
bpo-38881: choices() raises ValueError when all weights are zero (GH-17362)
-rw-r--r-- | Doc/library/random.rst | 8 | ||||
-rw-r--r-- | Lib/random.py | 4 | ||||
-rw-r--r-- | Lib/test/test_random.py | 5 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2019-11-22-20-03-46.bpo-38881.7HV1Q0.rst | 1 |
4 files changed, 15 insertions, 3 deletions
diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 1bd1856..933da3f 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -165,8 +165,9 @@ Functions for sequences The *weights* or *cum_weights* can use any numeric type that interoperates with the :class:`float` values returned by :func:`random` (that includes - integers, floats, and fractions but excludes decimals). Weights are - assumed to be non-negative. + integers, floats, and fractions but excludes decimals). Behavior is + undefined if any weight is negative. A :exc:`ValueError` is raised if all + weights are zero. For a given seed, the :func:`choices` function with equal weighting typically produces a different sequence than repeated calls to @@ -177,6 +178,9 @@ Functions for sequences .. versionadded:: 3.6 + .. versionchanged:: 3.9 + Raises a :exc:`ValueError` if all weights are zero. + .. function:: shuffle(x[, random]) diff --git a/Lib/random.py b/Lib/random.py index be4401c..e24737d 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -413,8 +413,10 @@ class Random(_random.Random): raise TypeError('Cannot specify both weights and cumulative weights') if len(cum_weights) != n: raise ValueError('The number of weights does not match the population') - bisect = _bisect total = cum_weights[-1] + 0.0 # convert to float + if total <= 0.0: + raise ValueError('Total of weights must be greater than zero') + bisect = _bisect hi = n - 1 return [population[bisect(cum_weights, random() * total, 0, hi)] for i in _repeat(None, k)] diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index f59c565..2c8c8e8 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -241,6 +241,11 @@ class TestBasicOps: choices = self.gen.choices choices(population=[1, 2], weights=[1e-323, 1e-323], k=5000) + def test_choices_with_all_zero_weights(self): + # See issue #38881 + with self.assertRaises(ValueError): + self.gen.choices('AB', [0.0, 0.0]) + def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In # particular, through 2.2.1 it failed to reset a piece of state used diff --git a/Misc/NEWS.d/next/Library/2019-11-22-20-03-46.bpo-38881.7HV1Q0.rst b/Misc/NEWS.d/next/Library/2019-11-22-20-03-46.bpo-38881.7HV1Q0.rst new file mode 100644 index 0000000..9f4a27d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-22-20-03-46.bpo-38881.7HV1Q0.rst @@ -0,0 +1 @@ +random.choices() now raises a ValueError when all the weights are zero. |