The Python Oracle

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

Become part of the top 3% of the developers by applying to Toptal https://topt.al/25cXVn

--

Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Game 2 Looping

--

Chapters
00:00 Question
02:13 Accepted answer (Score 4)
03:57 Thank you

--

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

Question links:
http://tpcg.io/inbKc8hK
https://repl.it/@DavidMoberg/ExemplaryTe...
https://www.reddit.com/r/learnpython/com.../

Accepted answer links:
[Here]: https://github.com/python/cpython/blob/f...
[right above]: https://github.com/python/cpython/blob/f...
[right below ]: https://github.com/python/cpython/blob/f...
[here]: https://github.com/python/cpython/blob/4...
[here]: https://github.com/python/cpython/blob/4...

--

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.