I would like to be able to model a 32-bit computer register as a class.
Furthermore I thought it would be neat to be able to derive from int for this. This works pretty well, except for the fact that integers are immutable and what I would like to do is to be able to update the class instance with a sliced value like this:
a = Register(0x555)
a[2:0] = 0x7
Is there any sort of hacky way I can do this?
I thought it would be useful to derive from int as I can treat the class object as a regular number in calculations etc. I also want to add more class attributes, possibly in the form of properties to do nice things for me.
So I anticipate that your answer will be that this cannot be done with a subclass of int due to its immutability. Therefore can you recommend a better way of doing this? Is there a more elegant solution to this that I am missing? Is this just a terrible idea in that my class is trying to do too much?
Let me also detail my end goal here. I wish to make a class that does the following:
- Pretend to be a number when referring to object as an object (no method calls)
- Be able to perform slicing for reads and writes (not so important)
- Be able to perform attribute access which will also update the resultant number. Why this? Well then the user of the class can then do e.g.
obj.bf0 = 2and this will updateobjwith the result. This way the user does not need to know the position of the bit field, just its name. I figured properties would serve well here.
So I think what I want to do is completely doable if I make a custom class which is not derived from int but then I lose the handy things that come for free from being derived from int.
Below was my first attempt, which I tried customizing from an already-created bitfield class
#
# bitfield manipulation
#
class bf(int):
def __getitem__(self, arg): #python 3 way
if type(arg) == int:
self.check_range(arg)
return (self >> arg) & 1
else: #assume a slice object
self.check_slice(arg)
if arg.start == arg.stop:
return self & (1 << arg.start)
mask = (2**(arg.start - arg.stop))-1
return self & mask
def check_range(self, arg):
if arg > 31 or arg < 0:
raise IndexError('Index can only be in the range from 0 to 31')
def check_slice(self, arg):
if arg.start > 31 or arg.stop > 31 or arg.stop < 0 or arg.stop < 0:
raise IndexError('Slice range is from 31 to 0')
if arg.start < arg.stop:
raise IndexError('Slice notation is: [5:2] not [2:5]')
def __setitem__(self,arg,value):
if type(arg) == int:
self.check_range(arg)
value = (value&1)<<arg
mask = (1)<<arg
self = bf((self & mask) | value)
else:
self.check_slice(arg)
mask = 2**(arg.start - arg.stop) - 1
value = (value & mask) << arg.stop
intermediate = (self & ~mask) | value
self = bf(intermediate)
Of course assigning to self doesn't work here as expected as whatever handle is referencing the object does get a new copy.
For example:
r = bf(0x55)
r[2:0] = 0x7
print(hex(r)) # results in 0x55