]> granicus.if.org Git - python/commitdiff
Document which part of the random module module are guaranteed.
authorRaymond Hettinger <python@rcn.com>
Tue, 7 Sep 2010 00:38:15 +0000 (00:38 +0000)
committerRaymond Hettinger <python@rcn.com>
Tue, 7 Sep 2010 00:38:15 +0000 (00:38 +0000)
Doc/library/random.rst
Lib/random.py
Lib/test/test_random.py
Misc/NEWS

index 6ef5d209fcf7734f48a3a416f16458594c7f66fe..270518c52bb0d943ea1d7f56827210c9fa12d38f 100644 (file)
@@ -51,18 +51,23 @@ from sources provided by the operating system.
 Bookkeeping functions:
 
 
-.. function:: seed([x])
+.. function:: seed([x], version=2)
 
-   Initialize the basic random number generator. Optional argument *x* can be any
-   :term:`hashable` object. If *x* is omitted or ``None``, current system time is used;
-   current system time is also used to initialize the generator when the module is
-   first imported.  If randomness sources are provided by the operating system,
-   they are used instead of the system time (see the :func:`os.urandom` function
-   for details on availability).
+   Initialize the random number generator.
 
-   If *x* is not ``None`` or an int, ``hash(x)`` is used instead. If *x* is an
-   int, *x* is used directly.
+   If *x* is omitted or ``None``, the current system time is used.  If
+   randomness sources are provided by the operating system, they are used
+   instead of the system time (see the :func:`os.urandom` function for details
+   on availability).
 
+   If *x* is an int, it is used directly.
+
+   With version 2 (the default), a :class:`str`, :class:`bytes`, or :class:`bytearray`
+   object gets converted to a :class:`int` and all of its bits are used.  With version 1,
+   the :func:`hash` of *x* is used instead.
+
+   .. versionchanged:: 3.2
+      Moved to the version 2 scheme which uses all of the bits in a string seed.
 
 .. function:: getstate()
 
index 592e4b899a17b9d218ec0d9f058785951389aeb7..4ff65ab8ea5776494722c05d20bf322c748353f6 100644 (file)
@@ -91,13 +91,17 @@ class Random(_random.Random):
         self.seed(x)
         self.gauss_next = None
 
-    def seed(self, a=None):
+    def seed(self, a=None, version=2):
         """Initialize internal state from hashable object.
 
         None or no argument seeds from current time or from an operating
         system specific randomness source if available.
 
-        If a is not None or an int, hash(a) is used instead.
+        For version 2 (the default), all of the bits are used if a is a str,
+        bytes, or bytearray.  For version 1, the hash() of a is used instead.
+
+        If a is an int, all bits are used.
+
         """
 
         if a is None:
@@ -107,6 +111,11 @@ class Random(_random.Random):
                 import time
                 a = int(time.time() * 256) # use fractional seconds
 
+        if version == 2 and isinstance(a, (str, bytes, bytearray)):
+            if isinstance(a, str):
+                a = a.encode("utf8")
+            a = int(_hexlify(a), 16)
+
         super().seed(a)
         self.gauss_next = None
 
index 10406fe32d3a44168a954611d7ab6f5d7d489169..78cd4d572d76f9e7eb2da0b678c6dddb03c458fe 100644 (file)
@@ -39,7 +39,7 @@ class TestBasicOps(unittest.TestCase):
             self.gen.seed(arg)
         for arg in [list(range(3)), dict(one=1)]:
             self.assertRaises(TypeError, self.gen.seed, arg)
-        self.assertRaises(TypeError, self.gen.seed, 1, 2)
+        self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4)
         self.assertRaises(TypeError, type(self.gen), [])
 
     def test_sample(self):
@@ -223,6 +223,21 @@ class SystemRandom_TestBasicOps(TestBasicOps):
 class MersenneTwister_TestBasicOps(TestBasicOps):
     gen = random.Random()
 
+    def test_guaranteed_stable(self):
+        # These sequences are guaranteed to stay the same across versions of python
+        self.gen.seed(3456147, version=1)
+        self.assertEqual([self.gen.random().hex() for i in range(4)],
+            ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1',
+             '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1'])
+        self.gen.seed("the quick brown fox", version=1)
+        self.assertEqual([self.gen.random().hex() for i in range(4)],
+            ['0x1.9ee265c177cdep-2', '0x1.bad51092e3c25p-1',
+             '0x1.85ff833f71576p-1', '0x1.87efb37462927p-1'])
+        self.gen.seed("the quick brown fox", version=2)
+        self.assertEqual([self.gen.random().hex() for i in range(4)],
+            ['0x1.1294009b9eda4p-2', '0x1.2ff96171b0010p-1',
+             '0x1.459e0989bd8e0p-5', '0x1.8b5f55892ddcbp-1'])
+
     def test_setstate_first_arg(self):
         self.assertRaises(ValueError, self.gen.setstate, (1, None, None))
 
index 014164ea7d7ce25f07357ac238b9e14da63885bc..a69064e2fdd9f148ef1ab81f47bc9c6e1e94cbe6 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@ Library
   * Document which parts of the module are guaranteed to stay the same
     across versions and which parts are subject to change.
 
+  * Update the seed() method to use all of the bits in a string
+    instead of just the hash value.
+
 - collections.OrderedDict now supports a new method for repositioning
   keys to either end.