页面导航:
英文教程的下载地址:
本篇文章是根据英文教程《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__ ,
_inst ,
seed ,
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模块里的
seed,
random,
uniform之类的随机函数其实就是
_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文件里:
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._seed及
print '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