How to instantiate in a lazy way?
Slaunger
Slaunger at gmail.com
Tue Dec 2 10:01:44 EST 2008
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:
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]
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__!
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.
I am still a novice Python programmer, and I may have overlooked
more Pythonic ways to do it.
-- Slaunger
More information about the Python-list
mailing list