Python sample can repeat values: without replacement refers to occurrences
By ON Lab · Sources checked November 8, 2026
random.sample selects occurrences without replacement. If the population contains equal values in different positions, the result can still contain equal values. random.choices samples with replacement, so the same occurrence can be selected again. Looking only at repeated displayed values can hide that distinction.
A historical reader compared sample and choices on [1, 1, 2, 2] and saw repeated values from both. We checked current Python documentation and tested ordinary sampling with a seeded Python 3.9.6 fixture. The examples explain the selection model, not the quality of a cryptographic generator or a guaranteed sequence across Python versions.
Label the positions to see the difference
import random
rng = random.Random(7)
population = [1, 1, 2, 2]
positions = rng.sample(range(len(population)), k=3)
values = [population[index] for index in positions]
assert len(set(positions)) == 3
assert len(set(values)) < 3
The positions are distinct, while only two different values exist in the population. Any three selected occurrences therefore include an equal value. That does not contradict sampling without replacement: the two occurrences of 1 are separate choices, as are the two occurrences of 2.
The current documentation explicitly permits repeated population members and treats each occurrence as selectable. It also says the original population is left unchanged. “Unique elements” in this context should not be expanded into a promise that equality-based duplicate values have been removed from the input.
Compare replacement using more draws than positions
positions_with_replacement = rng.choices(
range(len(population)), k=6
)
assert len(positions_with_replacement) == 6
assert len(set(positions_with_replacement)) < 6
With four possible positions and six draws, positions must repeat. choices permits that because it samples with replacement. A six-element result is not evidence that the population contained six different occurrences.
For sample, requesting five occurrences from a four-occurrence population raises ValueError. Our fixture verified that rejection. Choose the model according to the task rather than changing functions only to suppress the exception.
Use the keyword k with choices
draws = rng.choices(population, k=3)
The historical question shows choices(population, 3) as if 3 were a count. In the current API, the second positional argument is weights and the count is keyword-only k. Our local call with that positional integer raised TypeError. Use k=3 to express three draws; do not copy the posted shorthand and assume it was a verified current call.
Weights are another part of the model. If you use them, review their documented constraints and how they correspond to the population. This guide's fixture uses ordinary unweighted choices and does not test a weighted application.
If the task requires different values
distinct_population = list(dict.fromkeys(population))
assert distinct_population == [1, 2]
selected = rng.sample(distinct_population, k=2)
assert set(selected) == {1, 2}
For these hashable scalar values, this constructs a population of distinct values before sampling. It changes what the selectable units are. It is not the same population as four separate occurrences, and should be chosen deliberately.
There are only two different values here. Requesting three different values is impossible without changing the input or the requirement. Do not silently shorten the requested count and report that the original request was fulfilled.
The dict-based example needs hashable keys. For lists, records or application objects, define which field or equality relation makes two entries the same before choosing a deduplication method. A generic set conversion can also discard ordering information; the small example preserves the first occurrence order in the input.
Test properties rather than one lucky output
Our fixture checked distinct sampled positions, repeated values from those positions, allowed repeated choices, oversized-sample rejection and the keyword-argument boundary. A single choices result might look like a sample result. That visual coincidence is not a test of replacement semantics.
A seed helps reproduce a particular recorded fixture, but do not promise that every method returns an identical sequence across all future Python versions. Test the task's intended properties and record the runtime when an exact debugging sequence matters.
- Decide whether the selectable unit is an occurrence or a distinct value.
- Choose whether replacement is allowed.
- Check the requested count against the correct population.
- Use the documented function signature, including choices(..., k=...).
- Test position and value behavior separately on duplicated input.
Checked November 8, 2026 with Python 3.9.6. These are ordinary seeded sampling examples. No cryptographic use, weighted distribution study, performance claim or cross-version exact-sequence guarantee was tested.