Assigning a random value to a parameter in python program

I need to assign a random default to __init__() . For instance:

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

If I create 10 test instances, they all get exactly the same random value. I do not understand why this is happening. I know that I can assign a random value inside __init__() , but I'm curious why this happens. My first assumption was that the seed is the current time, and the objects are created too close to each other and, as a result, get the same random value. I created objects for 1 second from each other, but still the same result.

+4
source share
4 answers

The default value of the parameter is set when the function is created, and not when it is called - why is it the same every time.

A typical way to deal with this is to set the default parameter to None and test it using the 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 
+13
source

random.randrange(0, math.pow(2,128)-1) is evaluated when the function is defined, and not when it is called.

Use

 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 

instead.

+3
source

When you put an expression inside the constructor header (instead of the constructor body), it is evaluated only once.

Try this in body:

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

The reason that all instances of your class have the same value for the argument is because its default value is defined only once when the definition of the method in which it is part is compiled as part of the execution of the class statement.

Even though it was not intended for this, you can use Recipe 20.14 called “Automatically Initialize Attribute Attributes” from several Python Cookbook dated 2nd Edition to achieve what you want to do.

This applies to the sample code in your question:

 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 

Output Example:

 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 

Here's how it works:

The auto_attr class from the recipe is called descriptor . One of them is assigned to the attribute of the Test class named r . The first time this attribute is accessed in a Test instance using self.r , Python notes that it is associated with the handle and calls its __get__() method with the Test instance as an obj argument.

The descriptor method __get__() calls the associated factory function and assigns the result to an instance attribute with the same name, so that all further references to this attribute through the instance will get its actual value instead of Test class auto_attr .

+1
source

All Articles