在Python程序中为参数分配随机值

8 投票
4 回答
3399 浏览
提问于 2025-04-16 20:11

我想在 __init__() 里给一个默认的随机值。例如:

import math
import random
class Test:
    def __init__(self, r = random.randrange(0, math.pow(2,128)-1)):
        self.r = r
        print self.r

如果我创建10个 Test 的实例,它们都会得到完全一样的随机值。我不明白为什么会这样。我知道可以在 __init__() 里面给随机值,但我很好奇为什么会出现这种情况。我最开始的猜测是随机数的种子是当前时间,而对象创建得太接近了,所以结果是一样的。我试着把对象创建间隔1秒,但结果还是一样。

4 个回答

1

你所有类的实例都有相同参数值的原因是,因为这个默认值只在方法定义被编译时确定一次,也就是在执行 class 语句的时候。

虽然这不是它设计的初衷,但你可以使用《Python Cookbook, 2nd Edition》中第20.14条的内容,标题为实例属性的自动初始化,来实现你想要的效果。

下面是应用到你问题中的示例代码:

class AutoAttr(object):
    def __init__(self, name, factory, *args, **kwds):
        self.data = name, factory, args, kwds
    def __get__(self, obj, cls=None):
        name, factory, args, kwds = self.data
        setattr(obj, name, factory(*args, **kwds))
        return getattr(obj, name)

import math
import random

class Test(object):
    r = AutoAttr('r', random.randrange, 0, math.pow(2,128)-1)  # default value
    def __init__(self, r=None):
        if r is not None:  # argument value supplied to override default?
            self.r = r
        print format(self.r, ',d')

for i in xrange(5):
    Test()
Test(42)  # override default random initial value

示例输出:

282,608,676,427,101,189,083,121,399,193,871,110,434
211,475,719,281,604,076,410,306,973,803,289,140,631
86,842,148,927,120,143,765,936,219,265,140,532,918
41,767,122,731,332,110,507,836,985,804,081,250,336
97,993,619,669,833,151,963,441,072,354,430,500,011
42

这是它的工作原理:

这个来自食谱的 auto_attr 类被称为描述符。其中一个被分配给了 Test 类的一个属性,名为 r。第一次你在 Test 的实例中通过 self.r 访问这个属性时,Python 会注意到它绑定了一个描述符,并调用它的 __get__() 方法,同时将 Test 实例作为 obj 参数传入。

描述符的 __get__() 方法会调用相关的工厂函数,并将结果赋值给一个同名的实例属性,这样之后通过实例对这个属性的所有引用都会得到它的实际值,而不是 Test 类的 auto_attr 描述符。

5

这里的 random.randrange(0, math.pow(2,128)-1) 是在函数定义的时候就被计算了,而不是在你实际调用这个函数的时候。

所以,应该使用

class Test:
    def __init__(self, r = None):
        if r is None:
            r = random.randrange(0, math.pow(2,128)-1)
        self.r = r
        print self.r

来代替。

19

默认参数的值是在函数创建的时候就已经设定好的,而不是在函数被调用的时候设定的,所以每次调用的时候这个值都是一样的。

通常处理这个问题的方法是把默认参数设置为 None,然后用 if 语句来检查它。

import math
import random
class Test:
     def __init__(self, r = None):
             if r is None:
                 r = random.randrange(0, math.pow(2,128)-1)
             self.r = r
             print self.r

撰写回答