Adding Type Conversions
Our MutInt
class currently supports addition and comparison operations. However, it doesn't work with Python's built - in conversion functions such as int()
and float()
. These conversion functions are very useful in Python. For example, when you want to convert a value to an integer or a floating - point number for different calculations or operations, you rely on these functions. So, let's add the capabilities to our MutInt
class to work with them.
- Open
mutint.py
in the WebIDE and update it with the following code:
## mutint.py
from functools import total_ordering
@total_ordering
class MutInt:
"""
A mutable integer class that allows its value to be modified after creation.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialize with an integer value."""
self.value = value
def __str__(self):
"""Return a string representation for printing."""
return str(self.value)
def __repr__(self):
"""Return a developer - friendly string representation."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Support string formatting with format specifications."""
return format(self.value, fmt)
def __add__(self, other):
"""Handle addition: self + other."""
if isinstance(other, MutInt):
return MutInt(self.value + other.value)
elif isinstance(other, int):
return MutInt(self.value + other)
else:
return NotImplemented
def __radd__(self, other):
"""Handle reversed addition: other + self."""
return self.__add__(other)
def __iadd__(self, other):
"""Handle in - place addition: self += other."""
if isinstance(other, MutInt):
self.value += other.value
return self
elif isinstance(other, int):
self.value += other
return self
else:
return NotImplemented
def __eq__(self, other):
"""Handle equality comparison: self == other."""
if isinstance(other, MutInt):
return self.value == other.value
elif isinstance(other, int):
return self.value == other
else:
return NotImplemented
def __lt__(self, other):
"""Handle less - than comparison: self < other."""
if isinstance(other, MutInt):
return self.value < other.value
elif isinstance(other, int):
return self.value < other
else:
return NotImplemented
def __int__(self):
"""Convert to int."""
return self.value
def __float__(self):
"""Convert to float."""
return float(self.value)
__index__ = __int__ ## Support array indexing and other operations requiring an index
def __lshift__(self, other):
"""Handle left shift: self << other."""
if isinstance(other, MutInt):
return MutInt(self.value << other.value)
elif isinstance(other, int):
return MutInt(self.value << other)
else:
return NotImplemented
def __rlshift__(self, other):
"""Handle reversed left shift: other << self."""
if isinstance(other, int):
return MutInt(other << self.value)
else:
return NotImplemented
We've added three new methods to the MutInt
class:
__int__()
: This method is called when you use the int()
function on an object of our MutInt
class. For example, if you have a MutInt
object a
, and you write int(a)
, Python will call the __int__()
method of the a
object.
__float__()
: Similarly, this method is called when you use the float()
function on our MutInt
object.
__index__()
: This method is used for operations that specifically require an integer index. For instance, when you want to access an element in a list using an index, or perform bit - length operations, Python needs an integer index.
__lshift__()
: This method handles left shift operations when the MutInt
object is on the left side of the <<
operator.
__rlshift__()
: This method handles left shift operations when the MutInt
object is on the right side of the <<
operator.
The __index__
method is crucial for operations that demand an integer index, like list indexing, slicing, and bit - length operations. In our simple implementation, we set it to be the same as __int__
because our MutInt
object's value can be directly used as an integer index.
The __lshift__
and __rlshift__
methods are essential for supporting bitwise left shift operations. They allow our MutInt
objects to participate in bitwise operations, which is a common requirement for integer-like types.
- Create a new test file called
test_conversions.py
to test these new methods:
## test_conversions.py
from mutint import MutInt
## Create a MutInt object
a = MutInt(3)
## Test conversions
print(f"int(a): {int(a)}")
print(f"float(a): {float(a)}")
## Test using as an index
names = ['Dave', 'Guido', 'Paula', 'Thomas', 'Lewis']
print(f"names[a]: {names[a]}")
## Test using in bit operations (requires __index__)
print(f"1 << a: {1 << a}") ## Shift left by 3
## Test hex/oct/bin functions (requires __index__)
print(f"hex(a): {hex(a)}")
print(f"oct(a): {oct(a)}")
print(f"bin(a): {bin(a)}")
## Modify and test again
a.value = 4
print(f"\nAfter changing value to 4:")
print(f"int(a): {int(a)}")
print(f"names[a]: {names[a]}")
- Run the test script:
python3 /home/labex/project/test_conversions.py
You should see output similar to this:
int(a): 3
float(a): 3.0
names[a]: Thomas
1 << a: 8
hex(a): 0x3
oct(a): 0o3
bin(a): 0b11
After changing value to 4:
int(a): 4
names[a]: Lewis
Now our MutInt
class can be converted to standard Python types and used in operations that require an integer index.
The __index__
method is particularly important. It was introduced in Python to allow objects to be used in situations where an integer index is required, such as list indexing, bitwise operations, and various functions like hex()
, oct()
, and bin()
.
With these additions, our MutInt
class is now a fairly complete primitive type. It can be used in most contexts where a regular integer would be used, with the added benefit of being mutable.
Complete MutInt Implementation
Here's our complete MutInt
implementation with all the features we've added:
## mutint.py
from functools import total_ordering
@total_ordering
class MutInt:
"""
A mutable integer class that allows its value to be modified after creation.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialize with an integer value."""
self.value = value
def __str__(self):
"""Return a string representation for printing."""
return str(self.value)
def __repr__(self):
"""Return a developer - friendly string representation."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Support string formatting with format specifications."""
return format(self.value, fmt)
def __add__(self, other):
"""Handle addition: self + other."""
if isinstance(other, MutInt):
return MutInt(self.value + other.value)
elif isinstance(other, int):
return MutInt(self.value + other)
else:
return NotImplemented
def __radd__(self, other):
"""Handle reversed addition: other + self."""
return self.__add__(other)
def __iadd__(self, other):
"""Handle in - place addition: self += other."""
if isinstance(other, MutInt):
self.value += other.value
return self
elif isinstance(other, int):
self.value += other
return self
else:
return NotImplemented
def __eq__(self, other):
"""Handle equality comparison: self == other."""
if isinstance(other, MutInt):
return self.value == other.value
elif isinstance(other, int):
return self.value == other
else:
return NotImplemented
def __lt__(self, other):
"""Handle less - than comparison: self < other."""
if isinstance(other, MutInt):
return self.value < other.value
elif isinstance(other, int):
return self.value < other
else:
return NotImplemented
def __int__(self):
"""Convert to int."""
return self.value
def __float__(self):
"""Convert to float."""
return float(self.value)
__index__ = __int__ ## Support array indexing and other operations requiring an index
def __lshift__(self, other):
"""Handle left shift: self << other."""
if isinstance(other, MutInt):
return MutInt(self.value << other.value)
elif isinstance(other, int):
return MutInt(self.value << other)
else:
return NotImplemented
def __rlshift__(self, other):
"""Handle reversed left shift: other << self."""
if isinstance(other, int):
return MutInt(other << self.value)
else:
return NotImplemented
This implementation covers the key aspects of creating a new primitive type in Python. To make it even more complete, you could implement additional methods for other operations like subtraction, multiplication, division, etc.