Commit 9016f286 authored by Raymond Hettinger's avatar Raymond Hettinger

Issue #18844: Make the number of selections a keyword-only argument for random.choices().

parent 22805ca5
...@@ -124,7 +124,7 @@ Functions for sequences: ...@@ -124,7 +124,7 @@ Functions for sequences:
Return a random element from the non-empty sequence *seq*. If *seq* is empty, Return a random element from the non-empty sequence *seq*. If *seq* is empty,
raises :exc:`IndexError`. raises :exc:`IndexError`.
.. function:: choices(k, population, weights=None, *, cum_weights=None) .. function:: choices(population, weights=None, *, cum_weights=None, k=1)
Return a *k* sized list of elements chosen from the *population* with replacement. Return a *k* sized list of elements chosen from the *population* with replacement.
If the *population* is empty, raises :exc:`IndexError`. If the *population* is empty, raises :exc:`IndexError`.
......
...@@ -337,7 +337,7 @@ class Random(_random.Random): ...@@ -337,7 +337,7 @@ class Random(_random.Random):
result[i] = population[j] result[i] = population[j]
return result return result
def choices(self, k, population, weights=None, *, cum_weights=None): def choices(self, population, weights=None, *, cum_weights=None, k=1):
"""Return a k sized list of population elements chosen with replacement. """Return a k sized list of population elements chosen with replacement.
If the relative weights or cumulative weights are not specified, If the relative weights or cumulative weights are not specified,
......
...@@ -151,8 +151,8 @@ class TestBasicOps: ...@@ -151,8 +151,8 @@ class TestBasicOps:
# basic functionality # basic functionality
for sample in [ for sample in [
choices(5, data), choices(data, k=5),
choices(5, data, range(4)), choices(data, range(4), k=5),
choices(k=5, population=data, weights=range(4)), choices(k=5, population=data, weights=range(4)),
choices(k=5, population=data, cum_weights=range(4)), choices(k=5, population=data, cum_weights=range(4)),
]: ]:
...@@ -164,50 +164,50 @@ class TestBasicOps: ...@@ -164,50 +164,50 @@ class TestBasicOps:
with self.assertRaises(TypeError): # missing arguments with self.assertRaises(TypeError): # missing arguments
choices(2) choices(2)
self.assertEqual(choices(0, data), []) # k == 0 self.assertEqual(choices(data, k=0), []) # k == 0
self.assertEqual(choices(-1, data), []) # negative k behaves like ``[0] * -1`` self.assertEqual(choices(data, k=-1), []) # negative k behaves like ``[0] * -1``
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(2.5, data) # k is a float choices(data, k=2.5) # k is a float
self.assertTrue(set(choices(5, str_data)) <= set(str_data)) # population is a string sequence self.assertTrue(set(choices(str_data, k=5)) <= set(str_data)) # population is a string sequence
self.assertTrue(set(choices(5, range_data)) <= set(range_data)) # population is a range self.assertTrue(set(choices(range_data, k=5)) <= set(range_data)) # population is a range
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(2.5, set_data) # population is not a sequence choices(set_data, k=2) # population is not a sequence
self.assertTrue(set(choices(5, data, None)) <= set(data)) # weights is None self.assertTrue(set(choices(data, None, k=5)) <= set(data)) # weights is None
self.assertTrue(set(choices(5, data, weights=None)) <= set(data)) self.assertTrue(set(choices(data, weights=None, k=5)) <= set(data))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
choices(5, data, [1,2]) # len(weights) != len(population) choices(data, [1,2], k=5) # len(weights) != len(population)
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
choices(5, data, [0]*4) # weights sum to zero choices(data, [0]*4, k=5) # weights sum to zero
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(5, data, 10) # non-iterable weights choices(data, 10, k=5) # non-iterable weights
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(5, data, [None]*4) # non-numeric weights choices(data, [None]*4, k=5) # non-numeric weights
for weights in [ for weights in [
[15, 10, 25, 30], # integer weights [15, 10, 25, 30], # integer weights
[15.1, 10.2, 25.2, 30.3], # float weights [15.1, 10.2, 25.2, 30.3], # float weights
[Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights
[True, False, True, False] # booleans (include / exclude) [True, False, True, False] # booleans (include / exclude)
]: ]:
self.assertTrue(set(choices(5, data, weights)) <= set(data)) self.assertTrue(set(choices(data, weights, k=5)) <= set(data))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
choices(5, data, cum_weights=[1,2]) # len(weights) != len(population) choices(data, cum_weights=[1,2], k=5) # len(weights) != len(population)
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
choices(5, data, cum_weights=[0]*4) # cum_weights sum to zero choices(data, cum_weights=[0]*4, k=5) # cum_weights sum to zero
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(5, data, cum_weights=10) # non-iterable cum_weights choices(data, cum_weights=10, k=5) # non-iterable cum_weights
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(5, data, cum_weights=[None]*4) # non-numeric cum_weights choices(data, cum_weights=[None]*4, k=5) # non-numeric cum_weights
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
choices(5, data, range(4), cum_weights=range(4)) # both weights and cum_weights choices(data, range(4), cum_weights=range(4), k=5) # both weights and cum_weights
for weights in [ for weights in [
[15, 10, 25, 30], # integer cum_weights [15, 10, 25, 30], # integer cum_weights
[15.1, 10.2, 25.2, 30.3], # float cum_weights [15.1, 10.2, 25.2, 30.3], # float cum_weights
[Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights
]: ]:
self.assertTrue(set(choices(5, data, cum_weights=weights)) <= set(data)) self.assertTrue(set(choices(data, cum_weights=weights, k=5)) <= set(data))
def test_gauss(self): def test_gauss(self):
# Ensure that the seed() method initializes all the hidden state. In # Ensure that the seed() method initializes all the hidden state. In
......
...@@ -44,6 +44,10 @@ Library ...@@ -44,6 +44,10 @@ Library
- Issue #27897: Fixed possible crash in sqlite3.Connection.create_collation() - Issue #27897: Fixed possible crash in sqlite3.Connection.create_collation()
if pass invalid string-like object as a name. Patch by Xiang Zhang. if pass invalid string-like object as a name. Patch by Xiang Zhang.
- Issue #18844: random.choices() now has k as a keyword-only argument
to improve the readability of common cases and the come into line
with the signature used in other languages.
- Issue #18893: Fix invalid exception handling in Lib/ctypes/macholib/dyld.py. - Issue #18893: Fix invalid exception handling in Lib/ctypes/macholib/dyld.py.
Patch by Madison May. Patch by Madison May.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment