mirror of
https://github.com/python/cpython.git
synced 2025-08-08 10:58:51 +00:00
[3.12] gh-130285: Fix handling of zero or empty counts in random.sample() (gh-130291) (gh-130417)
This commit is contained in:
parent
91e5e246b3
commit
8db2fa2575
3 changed files with 21 additions and 5 deletions
|
@ -417,11 +417,11 @@ class Random(_random.Random):
|
||||||
cum_counts = list(_accumulate(counts))
|
cum_counts = list(_accumulate(counts))
|
||||||
if len(cum_counts) != n:
|
if len(cum_counts) != n:
|
||||||
raise ValueError('The number of counts does not match the population')
|
raise ValueError('The number of counts does not match the population')
|
||||||
total = cum_counts.pop()
|
total = cum_counts.pop() if cum_counts else 0
|
||||||
if not isinstance(total, int):
|
if not isinstance(total, int):
|
||||||
raise TypeError('Counts must be integers')
|
raise TypeError('Counts must be integers')
|
||||||
if total <= 0:
|
if total < 0:
|
||||||
raise ValueError('Total of counts must be greater than zero')
|
raise ValueError('Counts must be non-negative')
|
||||||
selections = self.sample(range(total), k=k)
|
selections = self.sample(range(total), k=k)
|
||||||
bisect = _bisect
|
bisect = _bisect
|
||||||
return [population[bisect(cum_counts, s)] for s in selections]
|
return [population[bisect(cum_counts, s)] for s in selections]
|
||||||
|
|
|
@ -224,8 +224,6 @@ class TestBasicOps:
|
||||||
sample(['red', 'green', 'blue'], counts=10, k=10) # counts not iterable
|
sample(['red', 'green', 'blue'], counts=10, k=10) # counts not iterable
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
sample(['red', 'green', 'blue'], counts=[-3, -7, -8], k=2) # counts are negative
|
sample(['red', 'green', 'blue'], counts=[-3, -7, -8], k=2) # counts are negative
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
sample(['red', 'green', 'blue'], counts=[0, 0, 0], k=2) # counts are zero
|
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
sample(['red', 'green'], counts=[10, 10], k=21) # population too small
|
sample(['red', 'green'], counts=[10, 10], k=21) # population too small
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
|
@ -233,6 +231,20 @@ class TestBasicOps:
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
sample(['red', 'green', 'blue'], counts=[1, 2, 3, 4], k=2) # too many counts
|
sample(['red', 'green', 'blue'], counts=[1, 2, 3, 4], k=2) # too many counts
|
||||||
|
|
||||||
|
# Cases with zero counts match equivalents without counts (see gh-130285)
|
||||||
|
self.assertEqual(
|
||||||
|
sample('abc', k=0, counts=[0, 0, 0]),
|
||||||
|
sample([], k=0),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
sample([], 0, counts=[]),
|
||||||
|
sample([], 0),
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
sample([], 1, counts=[])
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
sample('x', 1, counts=[0])
|
||||||
|
|
||||||
def test_choices(self):
|
def test_choices(self):
|
||||||
choices = self.gen.choices
|
choices = self.gen.choices
|
||||||
data = ['red', 'green', 'blue', 'yellow']
|
data = ['red', 'green', 'blue', 'yellow']
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Fix corner case for :func:`random.sample` allowing the *counts* parameter to
|
||||||
|
specify an empty population. So now, ``sample([], 0, counts=[])`` and
|
||||||
|
``sample('abc', k=0, counts=[0, 0, 0])`` both give the same result as
|
||||||
|
``sample([], 0)``.
|
Loading…
Add table
Add a link
Reference in a new issue