Lassen Sie uns dies mit dynamischer Typerstellung implementieren:
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)
Dadurch werden die Attribute überprüft, um festzustellen, ob sie gültig sind, bevor der Vorgang fortgesetzt werden kann.
Also ist das pickleable? Ja, wenn (und nur wenn) Sie Folgendes tun:
>>> 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
Die Definition muss sich in Ihrem Namespace befinden und lange genug vorhanden sein, damit pickle sie finden kann. Wenn Sie dies so definieren, dass es in Ihrem Paket enthalten ist, sollte es funktionieren.
Point = namedgroup("Point", ["x", "y"])
Pickle schlägt fehl, wenn Sie Folgendes tun oder die Definition temporär machen (z. B. außerhalb des Gültigkeitsbereichs, wenn die Funktion endet):
some_point = namedgroup("Point", ["x", "y"])
Und ja, die Reihenfolge der in der Typerstellung aufgeführten Felder bleibt erhalten.