Extending Python’s int type to accept only values within a given range
I would like to create a custom data type whi开发者_高级运维ch basically behaves like an ordinary int
, but with the value restricted to be within a given range. I guess I need some kind of factory function, but I cannot figure out how to do it.
myType = MyCustomInt(minimum=7, maximum=49, default=10)
i = myType(16) # OK
i = myType(52) # raises ValueError
i = myType() # i == 10
positiveInt = MyCustomInt(minimum=1) # no maximum restriction
negativeInt = MyCustomInt(maximum=-1) # no minimum restriction
nonsensicalInt = MyCustomInt() # well, the same as an ordinary int
Any hint is appreciated. Thanks!
Use __new__
to override the construction of immutable types:
def makeLimitedInt(minimum, maximum, default):
class LimitedInt(int):
def __new__(cls, x= default, *args, **kwargs):
instance= int.__new__(cls, x, *args, **kwargs)
if not minimum<=instance<=maximum:
raise ValueError('Value outside LimitedInt range')
return instance
return LimitedInt
Assignment in Python is a statement, not an expression, therefore there's no way to define assignment on a type since assigning rebinds the name completely. The best you could do is define a set()
method that takes the value that you want, at which point you can just create a "normal" class to handle the validation.
There is no need to define a new type:
def restrict_range(minimum=None, maximum=None, default=None, type_=int):
def restricted(*args, **kwargs):
if default is not None and not (args or kwargs): # no arguments supplied
return default
value = type_(*args, **kwargs)
if (minimum is not None and value < minimum or
maximum is not None and value > maximum):
raise ValueError
return value
return restricted
Example
restricted_int = restrict_range(7, 49, 10)
assert restricted_int("1110", 2) == 14
assert restricted_int(16) == 16
assert restricted_int() == 10
try:
restricted_int(52)
assert 0
except ValueError:
pass
You can derive a class from an int
in python e.g. class MyInt(int)
, but that type in python is immutable (you can't change the value) once it's created.
You could do something like this though:
class MyInt:
def __init__(self, i, max=None, min=None):
self.max = max
self.min = min
self.set(i)
def set(self, i):
if i > self.max: raise ValueError
if i < self.min: raise ValueError
self.i = i
def toInt(self):
return self.i
def __getattr__(self, name):
# Forward e.g. addition etc operations to the integer
# Beware that e.g. going MyInt(1)+MyInt(1)
# will return an ordinary int of "2" though
# so you'd need to do something like
# "result = MyInt(MyInt(1)+MyInt(1))
method = getattr(self.i, name)
def call(*args):
L = []
for arg in args:
if isinstance(arg, MyInt):
L.append(arg.toInt())
else: L.append(arg)
return method(*L)
return call
It may be better to use ordinary validation functions depending on what you want though if it's simpler.
EDIT: Is working now - Reverted back to a simpler earlier version, having addition etc functions which return other MyInt instances just isn't worth it :-)
精彩评论