Cryptographically random numbers

Paul Rubin http
Tue Mar 7 16:06:13 EST 2006


"Tuvas" <tuvas21 at gmail.com> writes:
> from os import urandom
> def cstring(bytes):
>     ret=''
>     while(len(ret)<bytes):
>         c=os.urandom(1)
>         if c>'0' and c<'z':
>             ret=ret+c
>     return ret
> 
> That should do it, though I bet there might be a more efficient way. 

One efficiency issue is that almost 90% of the os.urandom output gets
discarded.  The other is more generic so I thought I'd mention it:

    ret = ret + c

builds up a new string one character longer than the old value of ret,
then assigns ret to the new string.  That is, the first time through
the loop (when ret is empty) it builds a 1-char string, the next time
it builds a 2-char string, etc.  The total number of characters copied
around is approx 1+2+3+...+n where n is the final length, so it is
O(n**2).  If n is very large (not the case for something like a password)
this gets expensive.

The usual Python idiom for building up a string in approx linear time
is:

   def cstring(n):
      ret = []
      while (something):
        ret.append(generate_another_character())
      return ''.join(ret)

Python lists have a special efficiency hack so that ret.append doesn't
copy the whole list around, but rather, allocates space in bigger
chunks so that appending usually takes constant time.  I don't think
this is part of the official specification but it's deeply ingrained
in Python culture and all kinds of Python code relies on it.

Another approach is to use Python's StringIO class (like Java
StringBuffer objects), but that's never been popular.



More information about the Python-list mailing list