【问题标题】:Assigning random value to a parameter in a python program在python程序中为参数分配随机值
【发布时间】:2011-06-24 02:44:28
【问题描述】:

我需要在__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 秒的对象,但结果仍然相同。

【问题讨论】:

    标签: python random parameters init


    【解决方案1】:

    默认参数的值是在创建函数时设置的,而不是在调用时设置的 - 这就是为什么每次都一样。

    解决这个问题的典型方法是将默认参数设置为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
    

    【讨论】:

      【解决方案2】:

      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
      

      改为。

      【讨论】:

        【解决方案3】:

        当您将表达式放在构造函数头(而不是构造函数主体)中时,它只会被计算一次。

        在正文中试试这个:

        self.r = random.randrange(0,math.pow(0,128)-1)
        

        【讨论】:

        • 当表达式用于定义构造函数参数的默认值时为真,但是,一般来说,构造函数中的其他表达式在每次调用它初始化实例时都会被计算。
        • @martineau:你指的还有哪些其他表达方式?
        • @Ethan Furman:任何未用于定义构造函数参数之一的默认值的表达式将在每次调用时进行评估,例如出现在方法本身的主体中的表达式 - 例如显示在这个答案中。
        • @Ethan Furman:函数定义是一个复合可执行语句,并且在执行函数定义时评估默认参数值。这在 Python 文档的 Function Definitions 部分进行了说明。
        【解决方案4】:

        所有类实例的参数值都相同的原因是,它的默认值只确定一次,当它所属的方法定义被编译为 class 语句执行的一部分时。

        虽然这不是它的设计用途,但您可以使用标题为 Automatic Initialization of Instance Attributes 的配方 20.14 来自有点过时的 Python Cookbook, 2nd Edition 来实现您想要做的事情。

        这是应用于您问题中的示例代码:

        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 类称为descriptor。其中一个被分配给名为rTest class 属性。当您第一次使用 self.rTest 的实例中访问此属性时,Python 注意到它已绑定到一个描述符并使用 Test 实例作为 obj 参数调用其 __get__() 方法。

        描述符的__get__() 方法调用关联的工厂函数并将结果分配给具有相同名称的instance 属性,以便通过实例对该属性的所有进一步引用都将获得其实际值而不是 Test 类的 auto_attr 描述符。

        【讨论】:

          猜你喜欢
          • 2015-05-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多