Suppose I have a Python type with the t
property. I want to create a "parameterised metatype" such that the following works:
class MySuperClass(type):
pass
class MySubClass(MySuperClass):
# Here is the problem -- How do I define types that contain stuff,
# independent of an object?
def __init__(self, t): # Or __getitem__
self.t = t
def __instancecheck__(self, instance):
return isinstance(instance, MySubClass) and instance.t == self.t
def __subclasscheck__(self, subclass):
return MySubClass in subclass.__mro__ and subclass.t == self.t
class MyObject(metaclass=MySubClass):
def __init__(self, t):
self.t = t
# Test code:
## Both of these, square brackets work too
assert isinstance(MyObject(0), MySubClass(0))
assert not isinstance(MyObject(0), MySubClass(1))
## Ideally
assert isinstance(MyObject(0), MySuperClass) or isinstance(MyObject(0), MySubClass)
Currently I get the following error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-99ad08881526> in <module>
14 return MySubClass in subclass.__mro__ and subclass.t == self.t
15
---> 16 class MyObject(metaclass=MySubClass):
17 def __init__(self, t):
18 self.t = t
TypeError: __init__() takes 2 positional arguments but 4 were given
It is possible to meet the first part or requirement. But it will require an auxilliary checker class. A MySubClass is a descendant of type, MySubClass(0)
should be a class. Its is enough to create an internal class InstanceChecker
class in MySubClass
, and put the __instancecheck__
override their.
Code could be:
class MySubClass(MySuperClass):
def __new__(cls, name, bases=None, namespace=None, *args, **kwargs):
if bases is not None:
return super().__new__(cls, name, bases, namespace, **kwargs)
return cls.InstanceChecker(name)
class InstanceChecker:
def __init__(self, t):
self.t = t
def __instancecheck__(self, instance):
return isinstance(instance.__class__, MySubClass) and instance.t == self.t
class MyObject(metaclass=MySubClass):
def __init__(self, t):
self.t = t
# Test code:
## Both of these, square brackets work too
assert isinstance(MyObject(0), MySubClass(0))
assert not isinstance(MyObject(0), MySubClass(1))
BTW, I have removed the __subclasscheck__
override, because t
in only an instance attribute in MyObject
Alternatively, the metaclass can automatically add a super class in the bases
parameter. In the following code, MySuperClass
is no longer a superclass of MySubClass
but of MyObject
:
class MySuperClass():
pass
class MySubClass(type):
def __new__(cls, name, bases=None, namespace=None, *args, **kwargs):
if bases is not None:
return super().__new__(cls, name, bases + (MySuperClass,), namespace, **kwargs)
return cls.InstanceChecker(name)
class InstanceChecker:
def __init__(self, t):
self.t = t
def __instancecheck__(self, instance):
return isinstance(instance.__class__, MySubClass) and instance.t == self.t
class MyObject(metaclass=MySubClass):
def __init__(self, t):
self.t = t
# Test code:
## Both of these, square brackets work too
assert isinstance(MyObject(0), MySubClass(0))
assert not isinstance(MyObject(0), MySubClass(1))
## Ideally
assert isinstance(MyObject(0), MySuperClass)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With