The reason all your class instances have the same value for the argument is because its default value is determined only once, when the method definition it is part of is compiled as part of the class
statement's execution.
Although this is not the use it was designed for, you could use Recipe 20.14 titled Automatic Initialization of Instance Attributes from the somewhat dated Python Cookbook, 2nd Edition to achieve what you want to do.
This is it applied 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
Sample output:
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 a called a descriptor. One of them is assigned to a Test
class attribute named r
. The first time you access this attribute in an instance of Test
using self.r
, Python notices it's bound to a descriptor and calls its __get__()
method with the Test
instance as an obj
argument.
The descriptor's __get__()
method calls the associated factory function and assigns the result to an instance attribute with the same name, so that all further references to that attribute through the instance will get its actual value instead of the Test
class's auto_attr
descriptor.