Reputation: 2823
Is it possible to subclass int
and make it mutable?
Consider the following class:
class CustomBitVector(int):
# bit 7
@property
def seventh_property(self):
return bool(self & (1 << 7))
@seventh_property.setter
def seventh_property(self, val):
self |= bool(val) << 7
# bit 6
@property
def sixth_property(self):
return bool(self & (1 << 6))
@sixth_property.setter
def sixth_property(self, val):
self |= bool(val) << 6
# ... a few more of these ...
# bit 0
@property
def zeroth_property(self):
return bool(self & (1 << 0))
@zeroth_property.setter
def zeroth_property(self, val):
self |= bool(val) << 0
I trying to make a nice interface to a bit vector. I'm reading a proprietary protocol off of a socket and I've made classes to represent the messages I'm sending/receiving. Often these messages include bit vectors and it would be nice to deal with them like this.
This already works great for reading the bit vector values, but setting them doesn't work because int
is immutable.
If I rewrite one of the setters like:
@sixth_property.setter
def sixth_property(self, val):
print 'before:', self
self |= bool(val) << 6
print 'after:', self
Then I get this behavior:
In [2]: c = CustomBitVector()
In [3]: c.sixth_property
Out[3]: False
In [4]: c.sixth_property = True
before: 0
after: 64
In [5]: c
Out[5]: 0
In [6]: c.sixth_property
Out[6]: False
I can see my folly ... I'm assigning to self
instead of modifying it. How can I modify self
in this case?
Any crazy hack to make this happen? Maybe using a metaclass or something?
I forgot to mention a requirement:
Instances of CustomBitVector
must behave like int
. In particular, I need to be able to pass them to struct.pack
Upvotes: 3
Views: 4820
Reputation: 93
I just package up my proof-of-concept code and upload a mutableint package to PyPi.
https://pypi.org/project/mutableint/
Here is the usage.
from mutableint import MutableInt
# create a mutable integer with value 10
a = MutableInt(10)
# change the value of a to 11
a.set(11)
Upvotes: 1
Reputation: 93
Here is my solution. It also make an integer iteratable too.
import ctypes
class MutableInt(int):
def __init__(self, val = 0):
self._val = val
def __int__(self):
return self._val
def __index__(self):
return self._val
def __str__(self):
return str(self._val)
def __repr__(self):
return repr(self._val)
def __iter__(self):
self._iter_cnt = 0
return self
def __next__(self):
if self._iter_cnt == 0:
self._iter_cnt = 1
return self._val
else:
raise StopIteration
def set(self, val):
self._val = val
ob_digit = ctypes.c_long.from_address(id(self)+24)
ob_digit.value = val
Upvotes: -2
Reputation: 280291
Is it possible to subclass int and make it mutable?
Sort of. You can add all the mutable parts you want, but you can't touch the int parts, so the degree of mutability you can add won't help you.
Instead, don't use an int subclass. Use a regular object that stores an int. If you want to be able to pass it to struct.pack
like an int, implement an __index__
method to define how to interpret your object as an int:
class IntLike(object): # not IntLike(int):
def __init__(self, value=0):
self.value = value
def __index__(self):
return self.value
...
You can implement additional methods like __or__
for |
and __ior__
for in-place, mutative |=
. Don't try to push too hard for complete interoperability with ints, though; for example, don't try to make your objects usable as dict keys. They're mutable, after all.
If it's really important to you that your class is an int
subclass, you're going to have to sacrifice the c.sixth_property = True
syntax you want. You'll have to pick an alternative like c = c.with_sixth_property(True)
, and implement things non-mutatively.
Upvotes: 9