After researching on the __new__
function from MarkM's suggested link, that approach will do what I'm looking for, but I'm also questioning if it's the right approach. See both the discussion and solution sections following.
Discussion about subclassing base immutable tyeps
However, I'm also questioning the need to do this, at least for simple input validation checks. The nice thing about subclassing an immutable base class is that you get all the benefits of immutability, like built-in hashing, string representation, etc., that just inherits directly from the base class. But you would get the same benefits by simply adding a property to another class, add a setter function, and make it immutable.
The advantage with the subclassing is if you want immutable objects that all have the same input validation and/or additional methods that can be applied to many different classes/modules, without the additional complexity of creating a separate class which converts them into mutable objects, and then requires an additional level of property or method access (i.e. you make a "myint" object from a "MyInt" Class, but need a "value" property or something similar to access the underlying int, such as myint.value
rather than just myint
).
Solution / Implementation
So, there may be a better way to do this, but to answer my own question, here is a test script I wrote to at least get this working.
Note that int can have multiple arguments, when interpreting a string into a specific base, such as ('1111', 2), which converts the binary string '1111' to decimal 15. The base can be entered as a kwarg as well, so passing *args and **kwargs on in full to the int __new__
function is required.
Also, I ended up making the call to int first, before doing validation, so that it would convert floats and strings to int first, before attempting validation.
Note that since MyInt subclasses int, you must return an int value - you can't return a "None" on failure (though you could return a 0 or -1). This led me to raise a ValueError and handle errors back in the main script.
class MyInt(int):
def __new__(cls, *args, **kwargs):
value = super().__new__(cls, *args, **kwargs)
argstr = ', '.join([str(arg) for arg in args]) # Only for prototype test
print('MyInt({}); Returned {}'.format(argstr, value)) # Only for prototype test
if value < 0 or value > 100:
raise ValueError(' ERROR!! Out of range! Must be 0-100, was {}'.format(value))
return value
if __name__ == '__main__':
myint = MyInt('1111', 2) # Test conversion from binary string, base 2
print('myint = {} (type: {})\n'.format(myint, type(myint)))
for index, arg in enumerate([-99, -0.01, 0, 0.01, 0.5, 0.99, 1.5, 100, 100.1, '101']):
try:
a = MyInt(arg)
except ValueError as ex:
print(ex)
a = None
finally:
print(' a : {} = {}'.format(type(a), a))