Slaunger <[EMAIL PROTECTED]> wrote: > Just wanted to show the end result in its actual implementation! > > I ended up *not* making a decorator, as I already had a good idea > about how to do it > using __getattr__ > > class PayloadDualFrqIQOnDemand(PayloadDualFrqIQ): > """ > This class has the same interface as its parent, > but unlike its parent, it is instantiated without > its payload parsed up in its instance attributes > Q1, I1, Q2 and I2. Instead it stores a reference to > the file object in which the Payload data can be > read, the file position and > the version of the payload data. > > On accessing one of the data attributes, the actual > payload data are read from the file, and the reference to > the file object is unbound. > The constructor signature is therefore different from its > parent as it takes the file object, position and version > as arguments instead of the actual data. > """ > > @classmethod > def _unpack_from_file(cls, f, samples, ver): > bytes = samples * cls.bytes_per_sample > initial_pos = f.tell() > f.seek(initial_pos + bytes) #Skip over the payload > return cls(f, initial_pos, samples, ver) > > @classmethod > def unpack_from_ver3_file(cls, f, samples): > return cls._unpack_from_file(f, samples, ver=3) > > @classmethod > def unpack_from_ver4_file(cls, f, samples): > return cls._unpack_from_file(f, samples, ver=4) > > data_attr_names = frozenset(["Q1", "I1", "Q2", "I2"]) > > def __init__(self, a_file, a_file_position, samples, a_version): > """ > Returns an instance where the object knows where to > look for the payload but it will only be loaded on the > first attempt to read one of the data attributes > in a "normal" PayloadDualFrqIQ object. > """ > self.f = a_file > self.file_position = a_file_position > self.samples = samples > self.ver = a_version > > def __getattr__(self, attr_name): > """ > Checks if a request to read a non-existing data attribute > has an attribute corresponding to one of the data attributes > in a normal PayloadDualFrqIQ object. > > If true, the data attributes are created and bound to the > object using the file object instance, the file position > and the version. > > It is a prerequisite that the file object is still open. > The function leaves the file object at the file position > when it entered the method > > """ > cls = self.__class__ > if attr_name in cls.data_attr_names:
self.data_attr_names should do instead of cls.data_attr_names unless you are overriding it in the instance (which you don't appear to be). > initial_pos = self.f.tell() > try: > bytes = self.samples * cls.bytes_per_sample > self.f.seek(self.file_position) > buf = self.f.read(bytes) > if self.ver == 3: > bytes_to_data = cls._v3_byte_str_to_data > elif self.ver == 4: > bytes_to_data = cls._v4_byte_str_to_data > else: > raise TermaNotImplemented, \ > "Support for ver. %d not implemented." % > self.ver > I1, Q1, I2, Q2 = bytes_to_data(buf) > self.__dict__["I1"] = I1 > self.__dict__["Q1"] = Q1 > self.__dict__["I2"] = I2 > self.__dict__["Q2"] = Q2 > return self.__dict__[attr_name] I think you want setattr() here - __dict__ is an implemetation detail - classes with __slots__ for instance don't have a __dict__. I'd probably do this for k, v in zip(("I1", "Q1", "I2", "Q2"), bytes_to_data(buf)): setattr(self, k, v) return object.__getattr__(self, attr_name) That then has duplicated a list of "I1", "Q1" etc which needs to be factored out. > finally: > # Restore file position > self.f.seek(initial_pos) > # Unbind lazy attributes > del self.f > del self.ver > del self.file_position > del self.samples > > This seems to work out well. No infinite loops in __getattr__! :-) I would probably factor out the contents of the if statement into a seperate method, but that is a matter of taste! > At least it passes the unit test cases I have come up with so far. > > No guarantees though, as I may simply not have been smart enough to > bring forth unit test cases which make it crash. > > Comments on the code is still appreciated though. Looks fine! -- Nick Craig-Wood <[EMAIL PROTECTED]> -- http://www.craig-wood.com/nick -- http://mail.python.org/mailman/listinfo/python-list