Reputation: 3
I have a few classes with almost identical contents, so I tried two methods to copy the classes and their attributes over. The classes copy correctly, but the randint function is only invoked in the main class, so the same number is output every time. Is there any way to recalculate the random number for each class?
class a:
exampleData = random.randint(1,100)
b = type('b', a.__bases__, dict(a.__dict__))
class c(a):
pass
For example if a.exampleData = 50, b.exampleData and c.exampleData would be the same. Is there any way around this?
Edit -- Part of my program displays characters with random stats each time, and the class contains the stats associated with each character. The random numbers pick the stats out of a list, but the same stats are being chosen, instead of being random in each class. I may not be explaining this right, so basically:
data = [stat1,stat2,stat3,ect,,]
data[random.randint(1,3)]
Upvotes: 0
Views: 361
Reputation: 365767
While my other answer covers the question as asked, I suspect it's all completely unnecessary to the OP's actual problem.
If you just want to create a bunch of separate objects, which each have a separate value for exampleData
, you just want a bunch of instances of a single class, not a bunch of separate classes.
A class is a special kind of object that, in addition to doing all the normal object stuff, also works as a factory for other objects, which are instances of that class. You don't need a
, b
, and c
to all be factories for for different kinds of objects, you just need them to be different objects of the same type. So:
class RandomThing:
def __init__(self):
self.exampleData = random.randint(1,100)
a = RandomThing()
b = RandomThing()
… or, if you want to make sure b
is the same type of thing as a
but don't know what type that is:
b = type(a)()
That's as fancy as you need to get here.
See the official tutorial on Classes (or maybe search for a friendlier tutorial, because there are probably better ones out there).
Upvotes: 0
Reputation: 365767
When you write this:
b = type('b', a.__bases__, dict(a.__dict__))
… you're just copying a.__dict__
. Since a.__dict__
is just {'exampleData': 50}
, the new copy that ends up as b.__dict__
is also going to be {'exampleData': 50}
.
There are many ways you could get a new random number. The simplest is to just create a new random number for b
explicitly:
bdict = dict(a.__dict__)
b['exampleData'] = random.randint(1,100)
b = type('b', a.__bases__, bdict)
If you want to create a bunch of classes this way, you can wrap that up in a function:
def make_clone(proto, name):
clonedict = dict(proto.__dict__)
clonedict['exampleData'] = random.randint(1,100)
return type(name, proto.__bases__, clonedict)
You can make that factory function more complicated if you want to be (see namedtuple
for a pretty extreme example).
You could wrap that behavior up in a decorator:
def randomize(cls):
cls.exampleData = random.randint(1,100)
@randomize
class a:
pass
b = randomize(type('b', a.__bases__, dict(a.__dict__)))
Notice that I had to call the decorator with normal function-call syntax here, because there's no declaration statement to attach an @decorator
to.
Or you can wrap it up in a metaclass:
class RandomMeta(type):
def __new__(mcls, name, bases, namespace):
d = dict(namespace)
d['exampleData'] = random.randint(1,100)
return type.__new__(mcls, name, bases, d)
class a(metaclass=RandomMeta):
pass
b = type(a)('b', a.__bases__, dict(a.__dict__))
Notice that we have to call type(a)
here, the same way a class
definition statement does, not the base metaclass type
.
Also notice that I'm not taking **kwds
in the __new__
method, and I'm calling type.__new__
directly. This means that if you try to use RandomMeta
together with another metaclass (besides type
), you should get an immediate TypeError
, rather than something that may or may not be correct.
Meanwhile, I have a suspicion that what you're really trying to do here is build a prototype-based inheritance system, a la Self or JavaScript on top of Python's class-based system. While you can do that by using a special Prototype
metaclass and a bunch of class objects, it's a whole lot simpler to just have a Prototype
class and a bunch of instance objects. The only advantage to the metaclass approach is that you can use class
statements (misleadingly, but conveniently) to clone prototypes, and you're explicitly not doing that here.
Upvotes: 2