I am trying to subclass type
in order to create a class allowing to build specialized types. e.g. a ListType
:
>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...
However, this ListOfInt
will never be used to create instances ! I just use it as an instance of type
that I can manipulate to compare with other types ... In particular, in my case I need to look-up for a suitable operation, according to the type of input, and I need the type to contain more precisions (like list of int
or XML string
, etc ...).
So here's what I came up with :
class SpzType(type):
__metaclass__ = abc.ABCMeta
@classmethod
def __subclasshook__(cls, C):
return NotImplemented
def __new__(cls, base, **features):
name = 'SpzOf%s' % base.__name__
bases = (base,)
attrs = {}
return super(SpzType, cls).__new__(cls, name, bases, attrs)
def __init__(self, base, **features):
for name, value in features.items():
setattr(self, name, value)
The use of abc
is not obvious in the code above ... however if I want to write a subclass ListType
like in the example on top, then it becomes useful ...
The basic functionality actually works :
>>> class SimpleType(SpzType): pass
>>> t = SimpleType(int)
>>> issubclass(t, int)
True
>>> issubclass(int, t)
False
But when I try to check if t
is an instance of SpzType
, Python freaks out :
>>> isinstance(t, SpzType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)
I explored with pdb.pm()
what was going on, and I found out that the following code raises the error :
>>> SpzType.__subclasscheck__(SimpleType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)
WeIrD ?! Obviously there is an argument ... So what does that mean ? Any idea ? Did I misuse abc
?
Thanks to comment from kindall, I have refactored the code to the following :
class SpzType(abc.ABCMeta):
def __subclasshook__(self, C):
return NotImplemented
def __new__(cls, base, **features):
name = 'SpzOf%s' % base.__name__
bases = (base,)
attrs = {}
new_spz = super(SpzType, cls).__new__(cls, name, bases, attrs)
new_spz.__subclasshook__ = classmethod(cls.__subclasshook__)
return new_spz
def __init__(self, base, **features):
for name, value in features.items():
setattr(self, name, value)
So basically, SpzType
is now a subclass of abc.ABCMeta
, and subclasshook is implemented as an instance method. It works great and it is (IMO) elegant !!!
EDIT : There was a tricky thing ... because __subclasshook__
needs to be a classmethod, so I have to call the classmethod function manually... otherwise it doesn't work if I want to implement __subclasshook__
.