很多应用程式都会用到随机数,比如游戏里面,以及密码学相关的应用等等。下面就对Python所包含的一些常用的随机数函数进行介绍 下面会从Python源代码的角度来分析这些函数...

    页面导航: 英文教程的下载地址:

    本篇文章是根据英文教程《Python Tutorial》来写的学习笔记。该英文教程的下载地址如下:

    百度盘地址:http://pan.baidu.com/s/1c0eXSQG

    DropBox地址:点此进入DropBox链接

    Google Drive:点此进入Google Drive链接

    这是学习笔记,不是翻译,因此,内容上会与英文原著有些不同。以下记录和上一篇文章一样,也是根据英文教程的第八章来写的。(文章中的部分链接,可能需要通过代理访问!)

    本篇文章也会涉及到Python的C源代码,这些C源码都是2.7.8版本的。想使用gdb来调试python源代码的话,就需要按照前面"Python基本的操作运算符"文章中所说的,使用configure --with-pydebug命令来重新编译安装python。

概述:

    很多应用程式都会用到随机数,比如游戏里面,以及密码学相关的应用等等。下面就对Python所包含的一些常用的随机数函数进行介绍。

随机数函数:

    Python中常用的随机数函数如下表所示:

函数名 描述
choice(seq) 从列表之类的集合中获取一个随机的元素。
randrange ([start,] stop [,step]) 从start到stop之间(不包括stop在内)获取一个随机数
(返回的随机数默认是整数类型,当然也可以通过设置第四个参数来决定返回的类型,
比如浮点数类型,这里第四个参数没有显示出来),
step是start到stop之间的步进值。下面会进行举例说明。
random() 返回一个大于等于0,并且小于1的随机的浮点数。
seed([x]) 设置一个随机"种子",random函数会根据随机"种子"来生成随机数。
shuffle(lst) 将集合里的元素顺序打乱。
uniform(x, y) 返回大于等于x ,且小于y的随机的浮点数。

    下面会从Python源代码的角度来分析这些函数。

random模块:

    上面表格里的随机函数都是random模块里的函数,而random模块的源代码是以Python脚本的形式来编写的:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random
<module 'random' from '/usr/local/lib/python2.7/random.pyc'>
>>> quit()
[email protected]:~$ ls /usr/local/lib/python2.7/random.py
/usr/local/lib/python2.7/random.py  /usr/local/lib/python2.7/random.pyc
/usr/local/lib/python2.7/random.pyo
[email protected]:~$ 


    可以看到,random模块对应的python字节码文件为 /usr/local/lib/python2.7/random.pyc ,python中以pyc与pyo为扩展名的文件都属于字节码文件,这些都是编译后生成的文件,对应的源码文件是以py为扩展名的文件,这里就是 /usr/local/lib/python2.7/random.py ,该文件里定义了random模块对应的Python源代码:

.....................................................

__all__ = ["Random","seed","random","uniform","randint","choice","sample",
           "randrange","shuffle","normalvariate","lognormvariate",
           "expovariate","vonmisesvariate","gammavariate","triangular",
           "gauss","betavariate","paretovariate","weibullvariate",
           "getstate","setstate","jumpahead", "WichmannHill", "getrandbits",
           "SystemRandom"]

.....................................................

import _random

class Random(_random.Random):
	.....................................................

.....................................................

_inst = Random()
seed = _inst.seed
random = _inst.random
uniform = _inst.uniform
triangular = _inst.triangular
randint = _inst.randint
choice = _inst.choice
randrange = _inst.randrange
sample = _inst.sample
shuffle = _inst.shuffle
normalvariate = _inst.normalvariate
lognormvariate = _inst.lognormvariate
expovariate = _inst.expovariate
vonmisesvariate = _inst.vonmisesvariate
gammavariate = _inst.gammavariate
gauss = _inst.gauss
betavariate = _inst.betavariate
paretovariate = _inst.paretovariate
weibullvariate = _inst.weibullvariate
getstate = _inst.getstate
setstate = _inst.setstate
jumpahead = _inst.jumpahead
getrandbits = _inst.getrandbits

.....................................................


    当导入random模块时,上面这个脚本就会被执行,脚本中的__all___instseed random uniform之类的变量在被赋值后,就会成为random模块的属性成员,可以在Python命令行中查看到:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.__all__
['Random', 'seed', 'random', 'uniform', 'randint', 'choice', 
'sample', 'randrange', 'shuffle', 'normalvariate', 'lognormvariate', 
'expovariate', 'vonmisesvariate', 'gammavariate', 'triangular', 
'gauss', 'betavariate', 'paretovariate', 'weibullvariate', 
'getstate', 'setstate', 'jumpahead', 'WichmannHill', 'getrandbits', 
'SystemRandom']
>>> random._inst
<random.Random object at 0x829d114>
>>> random.seed
<bound method Random.seed of <random.Random object at 0x829d114>>
>>> random.random
<built-in method random of Random object at 0x829d114>
>>> random.uniform
<bound method Random.uniform of <random.Random object at 0x829d114>>
>>> quit()
[email protected]:~$  


    可以看到,random模块里的seedrandomuniform之类的随机函数其实就是_inst对象里绑定的函数(或者叫方法),_inst对象里的函数都是在random模块的Random类里实现的(可以从上面介绍的random.py文件中查看到):

....................................................

import _random

class Random(_random.Random):
	................................................
	def seed(self, a=None):
		................................................
	def randrange(self, start, stop=None, step=1, _int=int, _maxwidth=1L<<BPF):
		................................................
	def choice(self, seq):
		................................................
	def shuffle(self, x, random=None):
		................................................
	def uniform(self, a, b):
		................................................

....................................................


    上面所示的Random类是继承自_random模块里的Random类的,该_random模块则是通过import语句导入进来的,我们同样可以在Python命令行中查看到该模块对应的可执行文件位置:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random._random
<module '_random' from '/usr/local/lib/python2.7/lib-dynload/_random.so'>
>>> quit()
[email protected]:~$ 


    上面显示出_random模块对应的可执行文件是一个_random.so的动态库文件,该库文件所对应的Python的C源码,位于Modules/_randommodule.c文件里:

[email protected]:~/Downloads/Python-2.7.8$ ls Modules/_randommodule.c 
Modules/_randommodule.c
[email protected]:~/Downloads/Python-2.7.8$  


    Modules/_randommodule.c文件里的部分内容如下:

..................................................

static PyObject *
random_random(RandomObject *self)
{
    ..................................................
}

static PyObject *
random_seed(RandomObject *self, PyObject *args)
{
    ..................................................
}

static PyMethodDef random_methods[] = {
    {"random",          (PyCFunction)random_random,  METH_NOARGS,
        PyDoc_STR("random() -> x in the interval [0, 1).")},
    {"seed",            (PyCFunction)random_seed,  METH_VARARGS,
        PyDoc_STR("seed([n]) -> None.  Defaults to current time.")},
    ..................................................
    {NULL,              NULL}           /* sentinel */
};

static PyTypeObject Random_Type = {
	..................................................
	random_methods,                     /*tp_methods*/
	..................................................
};

PyDoc_STRVAR(module_doc,
"Module implements the Mersenne Twister random number generator.");

PyMODINIT_FUNC
init_random(void)
{
    PyObject *m;

    if (PyType_Ready(&Random_Type) < 0)
        return;
    m = Py_InitModule3("_random", NULL, module_doc);
    if (m == NULL)
        return;
    Py_INCREF(&Random_Type);
    PyModule_AddObject(m, "Random", (PyObject *)&Random_Type);
}


    当在python脚本里,通过import语句导入_random模块时,就会执行上面C文件里的init_random初始化函数。

    在init_random函数里,会通过PyModule_AddObject函数将Random类添加到_random模块中,_random模块的Random类所对应的C结构为Random_Type

    在Random_Type结构里的random_methods数组则定义了该类所包含的Python函数,以及这些Python函数在执行时会调用的底层的C函数。

    以上就是random模块与_random模块所对应的Python源代码。

seed函数:

    seed函数在/usr/local/lib/python2.7/random.py文件里的源代码如下:

import _random

class Random(_random.Random):
    ................................................

    def __init__(self, x=None):
        """Initialize an instance.

        Optional argument x controls seeding, as for Random.seed().
        """

        self.seed(x)
        self.gauss_next = None

    def seed(self, a=None):
        """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 or long, hash(a) is used instead.
        """

        if a is None:
            try:
                # Seed with enough bytes to span the 19937 bit
                # state space for the Mersenne Twister
                a = long(_hexlify(_urandom(2500)), 16)
            except NotImplementedError:
                import time
                a = long(time.time() * 256) # use fractional seconds

        super(Random, self).seed(a)
        self.gauss_next = None

................................................
_inst = Random()


    上面的__init__初始化函数会在_inst = Random()语句执行时被调用(该语句在导入random模块时就会被执行),在__init__函数里,又会通过self.seed(x)语句来调用Random类的seed函数,因此,在导入random模块时,一开始就会自动初始化一次随机"种子"。

    当seed函数的参数a(随机"种子"值)没有被提供时,它会使用_hexlify(_urandom(2500)即操作系统特定的随机源(如果实现了的话)来得到"种子"值,或者使用time.time()即当前的系统时间来得到"种子"值。在有了"种子"值后,就会通过super(Random, self).seed(a)语句将"种子"传递给super父类的seed方法。Random的父类前面提到过,就是_random模块里的Random类,该类的seed方法最终会去调用底层的random_seed所对应的C函数:

[email protected]:~$ gdb -q python
....................................................
>>> import random
....................................................
>>> random.seed()

Breakpoint 1, random_seed (self=0x829d05c, args=0xb7ca1cac)
    at /home/zengl/Downloads/Python-2.7.8/Modules/_randommodule.c:209
209	    PyObject *result = NULL;            /* guilty until proved innocent */
(gdb) c
Continuing.
[44558 refs]
>>> random.seed(123456)

Breakpoint 1, random_seed (self=0x829d05c, args=0xb7ca1c74)
    at /home/zengl/Downloads/Python-2.7.8/Modules/_randommodule.c:209
209	    PyObject *result = NULL;            /* guilty until proved innocent */
(gdb) c
Continuing.
[44558 refs]
>>> quit()
[20617 refs]

Program exited normally.
(gdb) q
[email protected]:~$ 


    读者可能在random.py源文件里还看到了一个同名的seed方法:

## -------------------- Wichmann-Hill -------------------

class WichmannHill(Random):

    VERSION = 1     # used by getstate/setstate

    def seed(self, a=None):
        """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 or long, hash(a) is used instead.

        If a is an int or long, a is used directly.  Distinct values between
        0 and 27814431486575L inclusive are guaranteed to yield distinct
        internal states (this guarantee is specific to the default
        Wichmann-Hill generator).
        """

        if a is None:
            try:
                a = long(_hexlify(_urandom(16)), 16)
            except NotImplementedError:
                import time
                a = long(time.time() * 256) # use fractional seconds

        if not isinstance(a, (int, long)):
            a = hash(a)

        a, x = divmod(a, 30268)
        a, y = divmod(a, 30306)
        a, z = divmod(a, 30322)
        self._seed = int(x)+1, int(y)+1, int(z)+1

        self.gauss_next = None


    上面的WichmannHill类是继承自前面提到的Random类的,要调用WichmannHill类的seed方法的话,需要先手动创建一个该类的实例(或者叫该类的对象):

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> myrand = random.WichmannHill()
>>> help(myrand.seed)

Help on method seed in module random:

seed(self, a=None) method of random.WichmannHill instance
    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 or long, hash(a) is used instead.
    
    If a is an int or long, a is used directly.  Distinct values between
    0 and 27814431486575L inclusive are guaranteed to yield distinct
    internal states (this guarantee is specific to the default
    Wichmann-Hill generator).
...................................................
[email protected]:~$ 


    如果想对该seed方法进行调试的话,可以在random.py文件里加入print语句:

root@zengl:~# vi /usr/local/lib/python2.7/random.py
class WichmannHill(Random):

    VERSION = 1     # used by getstate/setstate

    def seed(self, a=None):
        ..............................................

        a, x = divmod(a, 30268)
        a, y = divmod(a, 30306)
        a, z = divmod(a, 30322)
        self._seed = int(x)+1, int(y)+1, int(z)+1
        print(self._seed)
        print('hello world')

        self.gauss_next = None
..............................................

root@zengl:~# rm -rvf /usr/local/lib/python2.7/random.pyc
root@zengl:~# rm -rvf /usr/local/lib/python2.7/random.pyo
root@zengl:~# python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> myrand = random.WichmannHill()
(22053, 18044, 23534)
hello world
>>> myrand.seed(123456)
(2385, 5, 1)
hello world
>>> quit()
root@zengl:~# 


    上面先通过vi之类的编辑器在WichmannHill类的seed方法底部加入了两条print语句:print self._seedprint 'hello world' ,接着将random.pyc与random.pyo这些之前编译生成的字节码文件给删除掉(不删除好像也可以,应该会自动重新编译)。最后在python的交互式输入界面里,在执行seed方法时,就将上面脚本中的_seed元组,以及"hello world"的信息给显示出来了。这样就可以对random.py模块脚本进行调试了。

random函数:

    在之前提到的random.py文件里,并没有看到Random类里有random函数的相关定义,也就是说Random类并没有对random函数(或者叫random方法)进行重载,因此,在python里执行random.random()语句时,会直接执行Random类的父类里的random方法:

[email protected]:~$ gdb -q python
Reading symbols from /usr/local/bin/python...done.
(gdb) r
Starting program: /usr/local/bin/python 
[Thread debugging using libthread_db enabled]
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
[44533 refs]
>>> random.random()
Program received signal SIGINT, Interrupt. (按ctrl+c组合键来中断Python,并进入gdb调试界面)
0xb7ee09f8 in ___newselect_nocancel () from /lib/libc.so.6
(gdb) b random_random 
Breakpoint 1 at 0xb7c7cfda: file /home/zengl/Downloads/Python-2.7.8/Modules/_randommodule.c, line 140.
(gdb) c
Continuing.
(此处再按一次回车符,让上面的random.random()脚本得以执行,
并触发_randommodule.c里设置的断点)

Breakpoint 1, random_random (self=0x829d05c)
    at /home/zengl/Downloads/Python-2.7.8/Modules/_randommodule.c:140
140	    unsigned long a=genrand_int32(self)>>5, b=genrand_int32(self)>>6;
(gdb) n
141	    return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0));
(gdb) s
PyFloat_FromDouble (fval=0.35590543753820691) at Objects/floatobject.c:145
145	    if (free_list == NULL) {
(gdb) c
Continuing.
0.3559054375382069
>>> quit()
[20617 refs]

Program exited normally.
(gdb) q
[email protected]:~$ 


    从上面的输出中,可以看到,random.random()语句执行后,就在random_random函数里中断下来了,该函数就是random.Random类的父类即random._random.Random类里的random方法的底层C函数(感觉有点绕,不过它们确实就是这样的关系,在名称上面有很多同名的地方)。这里random.Random类指的是random模块里定义的Random类,而random._random.Random指的是random模块里导入的_random模块里的Random类。

    上面在执行完random_random函数后,就返回了0.3559054375382069的随机浮点数(该函数只会返回大于等于0,且小于1的随机浮点数)。

    此外,random方法不需要提供任何参数,如果手动提供参数的话,还会提示错误信息:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.random(123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: random() takes no arguments (1 given)
>>> random.random()
0.8643500108393966
>>> quit()
[email protected]:~$ 


    上面当给random方法提供了123的参数时,就产生了 TypeError: random() takes no arguments 的错误信息。

    Python里,其他的随机函数都是根据random方法来进行各种操作的。

choice函数:

    在之前提到的random.py文件里,可以看到choice函数的源代码:

import _random

class Random(_random.Random):
	................................................

    def choice(self, seq):
        """Choose a random element from a non-empty sequence."""
        return seq[int(self.random() * len(seq))]  # raises IndexError if seq is empty

	................................................


    上面的choice函数里,参数seq表示类似列表之类的集合,len(seq)会返回seq集合里的元素个数,int(self.random() * len(seq))会得到seq集合里的随机的索引值,这样,seq[int(self.random() * len(seq))]就表示从seq集合中根据随机的索引值,来获取集合里的元素。因此,choice函数的作用就是从seq集合里随机抽取出一个元素,并将该元素返回,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
5
>>> random.choice([1,2,3,4,5,6])
2
>>> random.choice([1,2,3,4,5,6])
5
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
6
>>> quit()
[email protected]:~$ 


    上例就通过choice函数,从[1,2,3,4,5,6]的列表中随机抽取出一个数字。

    此外,由于choice函数是根据索引值来获取集合里的元素的,因此,如果某个集合中不存在索引值的话,例如Dict(词典)中,当词典里所有的key都不是索引值时,就会报错:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.choice({'a':123, 'b':456, 'c':789})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/random.py", line 276, in choice
    return seq[int(self.random() * len(seq))]  # raises IndexError if seq is empty
KeyError: 2
>>> quit()
[email protected]:~$ 


    由于{'a':123, 'b':456, 'c':789}的词典里,所有的key都是字符串对象,因此当choice函数使用整数的索引值来作为key,在词典里进行查找时,就会抛出KeyError的错误。

    如果词典里,key为可供索引的整数对象的话,那么choice函数才可以正常执行:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.choice({0:123, 1:456, 2:789})
123
>>> random.choice({0:123, 1:456, 2:789})
456
>>> quit()
[email protected]:~$ 



randrange函数:

    randrange函数的源代码如下(在作者的系统里,该函数位于/usr/local/lib/python2.7/random.py文件中):

import _random

class Random(_random.Random):
	.....................................................

    def randrange(self, start, stop=None, step=1, _int=int, _maxwidth=1L<<BPF):
        """Choose a random item from range(start, stop[, step]).

        This fixes the problem with randint() which includes the
        endpoint; in Python this is usually not what you want.

        """

        # This code is a bit messy to make it fast for the
        # common case while still doing adequate error checking.
        istart = _int(start)
        if istart != start:
            raise ValueError, "non-integer arg 1 for randrange()"
        if stop is None:
            if istart > 0:
                if istart >= _maxwidth:
                    return self._randbelow(istart)
                return _int(self.random() * istart)
            raise ValueError, "empty range for randrange()"

        # stop argument supplied.
        istop = _int(stop)
        if istop != stop:
            raise ValueError, "non-integer stop for randrange()"
        width = istop - istart
        if step == 1 and width > 0:
            # Note that
            #     int(istart + self.random()*width)
            # instead would be incorrect.  For example, consider istart
            # = -2 and istop = 0.  Then the guts would be in
            # -2.0 to 0.0 exclusive on both ends (ignoring that random()
            # might return 0.0), and because int() truncates toward 0, the
            # final result would be -1 or 0 (instead of -2 or -1).
            #     istart + int(self.random()*width)
            # would also be incorrect, for a subtler reason:  the RHS
            # can return a long, and then randrange() would also return
            # a long, but we're supposed to return an int (for backward
            # compatibility).

            if width >= _maxwidth:
                return _int(istart + self._randbelow(width))
            return _int(istart + _int(self.random()*width))
        if step == 1:
            raise ValueError, "empty range for randrange() (%d,%d, %d)" % (istart, istop, width)

        # Non-unit step argument supplied.
        istep = _int(step)
        if istep != step:
            raise ValueError, "non-integer step for randrange()"
        if istep > 0:
            n = (width + istep - 1) // istep
        elif istep < 0:
            n = (width + istep + 1) // istep
        else:
            raise ValueError, "zero step for randrange()"

        if n <= 0:
            raise ValueError, "empty range for randrange()"

        if n >= _maxwidth:
            return istart + istep*self._randbelow(n)
        return istart + istep*_int(self.random() * n)

	.....................................................


    从上面的代码中可以看出来,当只提供了start参数时,该函数就会返回_int(self.random() * istart)的表达式的值,该表达式的含义就是:用start参数乘以0到1之间的随机浮点数,得到的值再通过_int转换为指定的类型(默认是整数类型),转换后的对象作为结果返回。简单的说就是:返回0到start之间的随机整数值(不包括start在内),如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.randrange(5)
4
>>> random.randrange(5)
0
>>> random.randrange(5)
3
>>> random.randrange(5)
3
>>> random.randrange(5)
2
>>> random.randrange(5)
1
>>> quit()
[email protected]:~$ 


    上面的例子中,只向randrange函数提供了一个参数5,这样,该函数就会返回0到5(不包括5在内)的随机整数。

    当向randrange函数提供了stop参数时,它会返回 _int(istart + _int(self.random()*width)) 或者 _int(istart + self._randbelow(width)) 的表达式的值,前者是常规情况下执行的表达式,后者则是当stop与start的差值过大时,而执行的表达式。式子里的width表示stop与start之间的差值。这两个表达式的含义就是:得到start到stop之间的随机整数值(不包括stop在内)。如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.randrange(5,10)
5
>>> random.randrange(5,10)
6
>>> random.randrange(5,10)
6
>>> random.randrange(5,10)
8
>>> random.randrange(5,10)
8
>>> random.randrange(5,10)
9
>>> random.randrange(5,10)
7
>>> quit()
[email protected]:~$ 


    上例中,通过random.randrange(5,10)语句,就可以返回5到10之间的随机整数值(不包括10在内)。

    如果向randrange函数提供了第三个step参数的话,当step为1时,返回的结果与上面只提供两个参数时的结果是一样的,当step不为1时,randrange函数就会执行底部的 istart + istep*self._randbelow(n) 或者 istart + istep*_int(self.random() * n) 的表达式。这两个表达式的含义就是:得到start到stop之间的以step为步进值的随机整数值,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.randrange(1, 10, 2)
5
>>> random.randrange(1, 10, 2)
3
>>> random.randrange(1, 10, 2)
1
.....................................................
>>> random.randrange(1, 10, 2)
7
.....................................................
>>> random.randrange(1, 10, 2)
9
>>> quit()
[email protected]:~$ 


    上例中,省略了重复的随机值,random.randrange(1, 10, 2)语句在执行时,返回了1、3、5、7、9的随机整数,这几个整数就是1到10之间的以2为步进值的随机整数(不包括10在内)。

    从上面randrange函数的Python源代码里,可以看到:该函数还可以接受第四个与第五个参数(第五个参数不常用,这里就不介绍了),第四个参数可以设置返回的对象类型,默认是整数类型,我们还可以将其设置为别的类型,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.randrange(1, 10, 1, float)
6.780220430817071
>>> random.randrange(1, 10, 1, float)
1.136255834454094
>>> random.randrange(1, 10, 1, float)
1.957636496718196
>>> random.randrange(1, 10, 1, float)
1.1314885451503913
>>> quit()
[email protected]:~$ 


    上例将float函数(该函数是内建模块里的函数,用于将参数转为浮点数),作为第四个参数传递给randrange方法,这样,就返回了1到10之间的随机浮点数了。

shuffle函数:

    shuffle函数的源代码如下(在作者的系统里,该函数位于/usr/local/lib/python2.7/random.py文件中):

import _random

class Random(_random.Random):
    .....................................................

    def shuffle(self, x, random=None):
        """x, random=random.random -> shuffle list x in place; return None.

        Optional arg random is a 0-argument function returning a random
        float in [0.0, 1.0); by default, the standard random.random.

        """

        if random is None:
            random = self.random
        _int = int
        for i in reversed(xrange(1, len(x))):
            # pick an element in x[:i+1] with which to exchange x[i]
            j = _int(random() * (i+1))
            x[i], x[j] = x[j], x[i]

    .....................................................


    从上面代码中,可以看出来,shuffle函数的作用其实就是:将参数x所对应的集合里的元素顺序给打乱,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> test = [1,2,3,4,5,6]
>>> random.shuffle(test)
>>> test
[1, 2, 6, 3, 5, 4]
>>> random.shuffle(test)
>>> test
[2, 4, 3, 6, 1, 5]
>>> random.shuffle(test)
>>> test
[1, 4, 6, 2, 5, 3]
>>> quit()
[email protected]:~$ 


    上例就通过shuffle函数将test列表里的元素顺序给打乱了,由于该函数是通过索引值来进行操作的,因此,如果集合无法使用索引值时,函数的执行就会出错,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> dt = {'a':123, 'b':456, 'c':789}
>>> random.shuffle(dt)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/random.py", line 292, in shuffle
    x[i], x[j] = x[j], x[i]
KeyError: 1
>>> quit()
[email protected]:~$ 


    由于 dt 词典里的key都是不能用于索引的字符串对象,因此,shuffle函数执行时,就产生了KeyError的错误。

    从上面shuffle函数的源代码中可以看到,该函数还可以接受第二个参数,并以该参数作为随机函数,对集合进行打乱操作,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> def myrand():
...  return 0.5
... 
>>> test = [1,2,3,4,5,6]
>>> random.shuffle(test, myrand)
>>> test
[1, 6, 2, 5, 3, 4]
>>> random.shuffle(test, myrand)
>>> test
[1, 4, 6, 3, 2, 5]
>>> random.shuffle(test, myrand)
>>> test
[1, 5, 4, 2, 6, 3]
>>> quit()
[email protected]:~$ 


    上面定义了一个myrand函数,该函数会以0.5作为返回结果,接着将该函数作为第二个参数传递给shuffle方法(也可以叫shuffle函数),这样,shuffle方法就会在内部使用myrand函数来打乱集合里的元素顺序了。

uniform函数:

    uniform函数的源代码如下(在作者的系统里,该函数位于/usr/local/lib/python2.7/random.py文件中):

import _random

class Random(_random.Random):
    .....................................................

    def uniform(self, a, b):
        "Get a random number in the range [a, b) or [a, b] depending on rounding."
        return a + (b-a) * self.random()

    .....................................................


    该函数的代码不难理解,就是返回参数a与参数b之间的随机浮点数,如下例所示:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.uniform(1, 10)
9.480760830000479
>>> random.uniform(1, 10)
1.5560346354496608
>>> random.uniform(1, 10)
7.575960535069615
>>> random.uniform(1, 10)
7.93923349939691
>>> random.uniform(1, 10)
6.560874919439973
>>> quit()
[email protected]:~$ 



本章结束语:

    从上面提到的random.py文件里,还可以看到很多其他的函数,如randint之类的,读者可以通过阅读这些函数的源代码来理解其作用。

    本章涉及到的类,继承,函数,重载等概念,只需了解即可,在以后的章节中,如果有机会的话,再做更多的解释。

    只有当理解了源代码时,我们才能更好的发挥这些函数的作用。

    限于篇幅,本章先到这里,下一篇将介绍Python里的三角函数。

    OK,休息,休息一下 o(∩_∩)o~~

    每个人都有潜在的能量,只是很容易:被习惯所掩盖,被时间所迷离,被惰性所消磨。

——  unknown
 
上下篇

下一篇: Python三角函数

上一篇: Python相关的数学运算函数

相关文章

Python定义和使用模块

Python基本的操作运算符

Python列表相关的脚本函数

Python三角函数

Python字符串相关的函数

Python中的异常