Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-40346: Add random.BaseRandom #19631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

bpo-40346: Add random.BaseRandom #19631

wants to merge 1 commit into from

Conversation

vstinner
Copy link
Member

@vstinner vstinner commented Apr 20, 2020

BaseRandom implements random() and randbytes() using getrandbits().
It has no state and its gauss() method is thread safe. It has no
VERSION attribute and its seed() method has no version parameter.

The implementation of random.Random, random.SystemRandom and
random.Random subclasses are not affected by this change.

Changes:

  • random.SystemRandom now inherits from BaseRandom instead of Random.
    It no longer inherits from _random.Random. An instance now only
    takes 48 bytes of memory, rather than 2568 bytes (on x86-64).
  • Move random() method implementation from SystemRandom to
    BaseRandom. random.Radom overrides it with _random.Random.random().
  • Move the gauss_next attribute of the gauss() method optimization to
    random.Random class.
  • Mark randbytes() parameter as positional-only.

https://bugs.python.org/issue40346

@vstinner
Copy link
Member Author

Random.seed() resets self.gauss_next attribute to None. Maybe BaseRandom.seed() should do it, since the attribute comes from BaseRandom not from Random. By the way, SystemRandom.seed() doesn't reset self.gauss_next to None: the method does nothing, as documented.

@vstinner
Copy link
Member Author

I'm not sure if BaseRandom.__init__subclass__() works as expected: it uses random() if it's implemented, but BaseRandom now implements random() from getrandbits().

cc @pitrou

@vstinner
Copy link
Member Author

I'm not sure if BaseRandom.init__subclass() works as expected: it uses random() if it's implemented, but BaseRandom now implements random() from getrandbits().

Ah, I checked: it works as expected :-) But it would be worth it to add tests on class inheritance, to check which method is called ;-)

@vstinner
Copy link
Member Author

TODO:

  • Decide if BaseRandom seed(), getstate() and setstate() should handle gauss_next, or if each subclass is supposed to deal with it.
  • Add unit tests.
  • Maybe add an example in the documentation which implements seed(), getstate() and setstate().

Example of subclass (it doesn't handle gauss_next):

class LCG(random.Random):
    def _rand_uint32(self):
        # Numerical Recipes LCG: generate 32 bits of entropy
        self._lcg = (self._lcg * 1664525 + 1013904223) % (2 ** 32)
        return self._lcg

    def seed(self, a=None):
        if a is not None:
            a = int(a & (2 ** 32 - 1))
        else:
            a = random.randint(0, 2 ** 32 - 1)
        self._lcg = a

    def getstate(self):
        return self._lcg

    def setstate(self, state):
        self._lcg = state

    def getrandbits(self, n):
        nword = n // 32
        ignore = n % 32
        if ignore:
            nword += 1
            ignore = 32 - ignore

        x = self._rand_uint32()
        for _ in range(nword - 1):
            x <<= 32
            x += self._rand_uint32()
        x >>= ignore
        return x

@vstinner
Copy link
Member Author

Decide if BaseRandom seed(), getstate() and setstate() should handle gauss_next, or if each subclass is supposed to deal with it.

Oh, in fact gauss_next only looks like an optimization. I moved it from BaseRandom to Random. BaseRandom.gauss() is now thread state. Random getstate() and setstate() don't have to bother with an attribute which came from its parent class.

BaseRandom implements random() and randbytes() using getrandbits().
It has no state and its gauss() method is thread safe. It has no
VERSION attribute and its seed() method has no version parameter.

The implementation of random.Random, random.SystemRandom and
random.Random subclasses are not affected by this change.

Changes:

* random.SystemRandom now inherits from BaseRandom instead of Random.
  It no longer inherits from _random.Random. An instance now only
  takes 48 bytes of memory, rather than 2568 bytes (on x86-64).
* Move random() method implementation from SystemRandom to
  BaseRandom. random.Radom overrides it with _random.Random.random().
* Move the gauss_next attribute of the gauss() method optimization to
  random.Random class.
* Mark randbytes() parameter as positional-only.
@vstinner
Copy link
Member Author

I rebased my PR and squashed commits to be able to edit the commit message.

New changes:

  • Add tests on BaseRandom subclasses
  • Remove BaseRandom.__init__() method: BaseRandom() no longer calls seed(). It's up to the subclass to decide what to do in the constructor.
  • Documentation completed.

@vstinner
Copy link
Member Author

@serhiy-storchaka, @tim-one: Ok, the PR is now ready for a review. Sorry, earlier versions of the PR were not complete. It was more complex than expected to implement this new base class.

@vstinner
Copy link
Member Author

vstinner commented Apr 29, 2020

I abandon my this PR and my PR #19700 since there is no clear consensus on these changes: see https://bugs.python.org/issue40346

I wrote PR #19797 instead: "Remove C implementation of Random.randbytes()".

@vstinner vstinner closed this Apr 29, 2020
@vstinner vstinner deleted the baserandom branch April 29, 2020 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants