I have the following class, and MyPy is reporting the problem NamedTuple type as an attribute is not supported
for the self.Data
attribute.
from collections import namedtuple
from collections.abc import MutableSequence
class Record(MutableSequence):
def __init__(self, recordname: str, fields: list, records=None):
if records is None:
records = []
self.fields = fields
defaults = [""] * len(fields)
self.Data = namedtuple(recordname, self.fields, defaults=defaults)
self._records = [self.Data(**record) for record in records]
self.valid_fieldnames = set(self.fields)
self.lookup_tables: dict = {}
def __getitem__(self, idx):
return self._records[idx]
def __setitem__(self, idx, val):
self._records[idx] = self.Data(**val)
def __delitem__(self, idx):
del self._records[idx]
def __len__(self):
return len(self._records)
def insert(self, idx, val):
a = self._records[:idx]
b = self._records[idx:]
if isinstance(val, self.Data):
self._records = a + [val] + b
else:
self._records = a + [self.Data(**val)] + b
I don't understand what the issue is that MyPy is reporting on, and haven't had any luck looking online. The code actually runs as expected without any errors; it's only MyPy that reports an issue and I'm just curious to understand what it is.
Some additional info: I am using Python-3.12 in VS Code with the official MyPy Type Checker extension, which is what's reporting the problem.
I tried a few things to see if the error would go away, but none of them worked. For example, I removed MutableSequence
as a parent class, I deleted the defaults from the namedtuple constructor, and I removed all the dunder methods, but the error persisted.
This makes a lot of sense if you consider how namedtuple works. It's a code-generator, and it creates a new type.
Therefore, if you use it during the class init like this, you will be generating a new type per-instance, and different instances of Record
will have different types for self.Data
, even if they have the same typename and fields.
A static type checker such as Mypy can not do anything useful with attribute value types being created dynamically at runtime. Further, the if isinstance(val, self.Data)
logic in the insert
method is likely to have some unpleasant surprises, because the types associated with different record instances will have separate identities.