class PseudoStructure(object): """A fast, small-footprint, structure-like type suitable for use as a data transfer or parameter object. This class is not intended to be used directly. Subclasses should define the fields they care about as slots and (optionally) define an initializer for those fields. This class is compatible with both Python2 and Python3 (tested on 2.4-3.3). """ __slots__ = [] @classmethod def define(cls, class_name, *field_names): r"""Dynamically create a class definition for a subclass of ``PseudoStructure``. Note that objects of a dynamically created class cannot be pickled. If pickling support is needed, the subclass must have a static definition. >>> X = PseudoStructure.define('Attribute', 'name', 'value') >>> X.__name__ 'Attribute' >>> issubclass(X, PseudoStructure) True >>> X.__slots__ ('name', 'value') """ return type(class_name, (cls,), {"__slots__": field_names}) def __getattr__(self, name): r"""Lazily initialize the value for *name*. >>> class Attribute(PseudoStructure): ... __slots__ = ['name', 'value'] ... >>> attr = Attribute() >>> attr.name is None True >>> attr.description is None Traceback (most recent call last): ... AttributeError: 'Attribute' object has no attribute 'description' """ # only called once, if a value has not yet been assigned to the slot; # returns None as a reasonable default; if the name is not a slot, # raises AttributeError as expected setattr(self, name, None) def __getstate__(self): r"""Build the state object for pickling this object. >>> class Attribute(PseudoStructure): ... __slots__ = ['name', 'value'] ... >>> attr = Attribute() >>> attr.name = 'test' >>> sorted(attr.__getstate__().items()) [('name', 'test'), ('value', None)] """ state = {} for cls in self.__class__.__mro__: slots = getattr(cls, "__slots__", []) if (isinstance(slots, str)): slots = [slots] for slot in slots: state[slot] = getattr(self, slot) return state def __setstate__(self, state): r"""Initialize this object from a pickling state object. >>> class Attribute(PseudoStructure): ... __slots__ = ['name', 'value'] ... >>> attr = Attribute() >>> attr.name = 'test' >>> state = attr.__getstate__() >>> attr2 = Attribute() >>> attr2.__setstate__(state) >>> attr2.name == 'test' True >>> attr2.value is None True """ for (name, value) in state.items(): setattr(self, name, value) def __eq__(self, other): r"""Return True if the internal states of *other* and self are equal. >>> class Sample(PseudoStructure): ... __slots__ = ['value'] ... >>> sample1 = Sample() >>> sample2 = Sample() >>> sample1 == sample2 True >>> sample2.value = 'test' >>> sample1 == sample2 False """ return (self.__getstate__() == other.__getstate__()) def __hash__(self): r"""Return the hash of this pseudo-structure's internal state. >>> class Sample(PseudoStructure): ... __slots__ = ['value'] ... >>> sample1 = Sample() >>> sample2 = Sample() >>> hash(sample1) == hash(sample2) True >>> sample2.value = 'test' >>> hash(sample1) == hash(sample2) False """ return hash(tuple(sorted(self.__getstate__().items()))) def __repr__(self): r"""Return an unambiguous description of this object. >>> class Sample(PseudoStructure): ... __slots__ = ['value'] ... >>> sample = Sample() >>> repr(sample) "<Sample {'value': None}>" """ return "<%s %r>" % (self.__class__.__name__, self.__getstate__()) if (__name__ == "__main__"): import doctest import pickle doctest.testmod() class Test(PseudoStructure): __slots__ = ["name"] x = Test() x.name = "pickle test" y = pickle.loads(pickle.dumps(x)) assert x == y