I would like to have for exemple a posint
class that inherit from int
, but with a custom behaviour when calling isinstance()
:
>>> isinstance(1, int), isinstance(1, posint)
(True, True)
>>> isinstance(-1, int), isinstance(-1, posint)
(True, False)
I tried first:
class posint(int):
def __instancecheck__(self, obj):
try:
obj >= 0
return True
except:
return False
But the __instancecheck__
have to be declared in a metaclass.
So I end up with this heavy and ugly thing:
class NewType(type):
def __instancecheck__(self, obj):
try:
obj >= 0
return True
except:
return False
class posint(metaclass=NewType):
pass
It works, but it cannot be the good solution... Doesn't work with any other checking, doesn't support inheritance...
After that I managed to implement something better:
class CheckedType(type):
def __instancecheck__(cls, obj):
if not all(isinstance(obj, base) for base in cls.mro()[1:-1]):
return False
return cls.__instancecheck__(obj)
class posint(int, metaclass=CheckedType):
@classmethod
def __instancecheck__(cls, obj):
if obj >= 0:
return True
return False
But this seems an abuse of __instancecheck__
.
I was thinking we can use use something from the abc
or the typing
module...
Any ideas?
A this time, after some experimentations, I'm using this recipe:
class TypedMeta(type):
"""Metaclass used for custom types."""
def __instancecheck__(cls, obj):
return cls._test(obj)
@staticmethod
def _init(self, x):
if not self._test(x):
raise ValueError(f"bad value for '{self.__class__.__name__}' object")
def __init__(cls, name, bases, clsdict):
if not clsdict.get('_test'):
raise TypeError(f"cannot instaciate '{name}' class without '_test' method")
setattr(cls, '__init__', TypedMeta._init)
class posint(int, metaclass=TypedMeta):
"""Strictly positive 'int'."""
@classmethod
def _test(cls, val):
return val > 0
So even if somebody wants to instantiate an object of this type, or to cast another into it, it will perform the _test
method first.