The Python Oracle

Why does wrapping the random method of random.Random seem to affect the RNG?

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Meditation

--

Chapters
00:00 Why Does Wrapping The Random Method Of Random.Random Seem To Affect The Rng?
01:22 Accepted Answer Score 4
02:38 Thank you

--

Full question
https://stackoverflow.com/questions/6327...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #python3x #random

#avk47



ACCEPTED ANSWER

Score 4


This answer is about Python 3.6.

The state is only changed by the gen.randint(1, 1) call, so let's see what it calls under the hood.

  1. Here is the implementation of random.Random.randint:

    def randint(self, a, b):
        """Return random integer in range [a, b], including both end points.
        """
    
        return self.randrange(a, b+1)
    
  2. random.Random.randrange is right above, and it generates random numbers using random.Random._randbelow...

  3. ...which is implemented right below randint, and checks whether the random method has been overridden!

Looks like _randbelow is the issue:

...
from types import MethodType as _MethodType, BuiltinMethodType as _BuiltinMethodType
...


def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,
               Method=_MethodType, BuiltinMethod=_BuiltinMethodType):
    "Return a random int in the range [0,n).  Raises ValueError if n==0."

    random = self.random
    getrandbits = self.getrandbits

    # CHECKS IF random HAS BEEN OVERRIDDEN!

    # If not, this is the default behaviour:

    # Only call self.getrandbits if the original random() builtin method
    # has not been overridden or if a new getrandbits() was supplied.
    if type(random) is BuiltinMethod or type(getrandbits) is Method:
        k = n.bit_length()  # don't use (n-1) here because n can be 1
        r = getrandbits(k)          # 0 <= r < 2**k
        while r >= n:
            r = getrandbits(k)
        return r

    # OTHERWISE, it does something different!

    # There's an overridden random() method but no new getrandbits() method,
    # so we can only use random() from here.

    # And then it goes on to use `random` only, no `getrandbits`!

(For reference, getrandbits is implemented in C here, and random is also implemented in C here.)

So there's the difference: the randint method ends up behaving differently depending on whether random has been overridden or not.