1

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:

  1. Pretend to be a number when referring to object as an object (no method calls)
  2. Be able to perform slicing for reads and writes (not so important)
  3. 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 = 2 and this will update obj with 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
Gregory Kuhn
  • 1,627
  • 2
  • 22
  • 34

0 Answers0