NumPy maintains the legacy RandomState class and module-level functions for backward compatibility. However, new code should use Generator and default_rng().
The legacy API is maintained for backward compatibility but will not receive new features. Use Generator for new code.
Legacy module-level functions use a single global RandomState instance:
import numpy as np# These all use the same global RandomStatenp.random.seed(42)x = np.random.random(5)y = np.random.normal(0, 1, 5)z = np.random.randint(0, 10, 5)
Global functions share state across your entire program, making debugging and testing difficult. Use explicit Generator instances instead.
np.random.normal(loc=0, scale=1, size=None)np.random.uniform(low=0, high=1, size=None)np.random.exponential(scale=1, size=None)np.random.binomial(n, p, size=None)np.random.poisson(lam=1, size=None)# ... and many more
import numpy as np# Set seed for reproducibilitynp.random.seed(42)print(np.random.random(3))# [0.37454012 0.95071431 0.73199394]# Reset to same seednp.random.seed(42)print(np.random.random(3))# [0.37454012 0.95071431 0.73199394] # Same values
state = np.random.get_state() # Save statenp.random.set_state(state) # Restore state
Example:
import numpy as npnp.random.seed(42)print("Before:", np.random.random(3))# Save statestate = np.random.get_state()print("Middle:", np.random.random(3))# Restore statenp.random.set_state(state)print("After:", np.random.random(3)) # Same as "Middle"
import numpy as npdata = np.arange(10)# === OLD WAY ===np.random.seed(42)old_shuffled = data.copy()np.random.shuffle(old_shuffled)# === NEW WAY ===rng = np.random.default_rng(42)new_shuffled = data.copy()rng.shuffle(new_shuffled)print(f"Old: {old_shuffled}")print(f"New: {new_shuffled}")
import numpy as np# === OLD WAY (Problematic) ===def simulation_old(seed): np.random.seed(seed) return np.random.normal(size=1000).mean()# Each call reseeds the global state!result1 = simulation_old(42)result2 = simulation_old(43)# === NEW WAY (Better) ===def simulation_new(rng): return rng.normal(size=1000).mean()# Create independent generatorsrng_parent = np.random.default_rng(42)rng1, rng2 = rng_parent.spawn(2)result1 = simulation_new(rng1)result2 = simulation_new(rng2)# Or use SeedSequencefrom numpy.random import SeedSequence, Generator, PCG64ss = SeedSequence(42)child_seeds = ss.spawn(2)rngs = [np.random.default_rng(s) for s in child_seeds]results = [simulation_new(rng) for rng in rngs]
import numpy as npimport pytestdef test_with_random(): # Save original state state = np.random.get_state() try: # Your test with seeded random np.random.seed(42) result = function_using_random() assert result == expected finally: # Restore state np.random.set_state(state)
Better approach - refactor to accept RNG:
import numpy as npdef function_with_rng(rng=None): if rng is None: rng = np.random.default_rng() return rng.normal(size=100)def test_function(): rng = np.random.default_rng(42) result = function_with_rng(rng) assert result.shape == (100,)
# DEPRECATEDnp.random.random_integers(low, high, size) # Use integers(..., endpoint=True)np.random.ranf(size) # Use random()np.random.sample(size) # Use random()