Let me implement this with the creation of a dynamic type:
import copy def namedgroup(typename, fieldnames): def init(self, **kwargs): attrs = {k: None for k in self._attrs_} for k in kwargs: if k in self._attrs_: attrs[k] = kwargs[k] else: raise AttributeError('Invalid Field') self.__dict__.update(attrs) def getattribute(self, attr): if attr.startswith("_") or attr in self._attrs_: return object.__getattribute__(self, attr) else: raise AttributeError('Invalid Field') def setattr(self, attr, value): if attr in self._attrs_: object.__setattr__(self, attr, value) else: raise AttributeError('Invalid Field') def rep(self): d = ["{}={}".format(v,self.__dict__[v]) for v in self._attrs_] return self._typename_ + '(' + ', '.join(d) + ')' def iterate(self): for x in self._attrs_: yield self.__dict__[x] raise StopIteration() def setitem(self, *args, **kwargs): return self.__dict__.__setitem__(*args, **kwargs) def getitem(self, *args, **kwargs): return self.__dict__.__getitem__(*args, **kwargs) attrs = {"__init__": init, "__setattr__": setattr, "__getattribute__": getattribute, "_attrs_": copy.deepcopy(fieldnames), "_typename_": str(typename), "__str__": rep, "__repr__": rep, "__len__": lambda self: len(fieldnames), "__iter__": iterate, "__setitem__": setitem, "__getitem__": getitem, } return type(typename, (object,), attrs)
This checks the attributes to make sure they are valid before allowing the operation to continue.
So is it legible? Yes, if (and only if) you do the following:
>>> import pickle >>> Point = namedgroup("Point", ["x", "y"]) >>> p = Point(x=100, y=200) >>> p2 = pickle.loads(pickle.dumps(p)) >>> p2.x 100 >>> p2.y 200 >>> id(p) != id(p2) True
The definition must be in your namespace and must exist long enough to breed to find it. Therefore, if you defined this in your package, it should work.
Point = namedgroup("Point", ["x", "y"])
Pickle will not work if you do the following, or make the definition temporary (out of scope when the function ends, say):
some_point = namedgroup("Point", ["x", "y"])
And yes, it preserves the order of the fields listed in the creation type.