dynamic type changing
Bruno Desthuilliers
bdesth.quelquechose at free.quelquepart.fr
Sat May 27 15:40:22 EDT 2006
andychambers2002 at yahoo.co.uk a écrit :
> I'm working on a "TempFile" class that stores the data in memory until
> it gets larger than a specified threshold (as per PEP 42). Whilst
> trying to implement it, I've come across some strange behaviour. Can
> anyone explain this?
>
> The test case at the bottom starts a TempFile at size 50 and prints its
> type. It then increases the size to the threshold at which point
> "self" is changed to being a TemporaryFile.
Changed how ?-)
> It seems that the next
> call correctly uses the write() method of TemporaryFile (since we don't
> see "changing type" in the output). However, type(tmp) still equals
> TempFile. Not only that, tmp can still access the method dummy() that
> exists only in TempFile.
>
> #!/usr/bin/env python
> from StringIO import StringIO
> import tempfile
>
> class TempFile(StringIO, object):
> """A temporary file implementation that uses memory unless
> either capacity is breached or fileno is requested, at which
> point a real temporary file will be created and the relevant
> details returned
> """
> def __init__(self, buffer, capacity):
> """Creates a TempFile object containing the specified buffer.
> If capacity is specified, we use a real temporary file once the
>
> file gets larger than that size. Otherwise, the data is stored
>
> in memory.
> """
> self.capacity = capacity
> if len(buffer) > capacity:
> self = tempfile.TemporaryFile()
assigning to 'self' in a method doesn't impact the object itself - it
only rebinds the *local* name 'self' for the rest of the block.
If you want to change the class of an object, you must assign to
self.__class__ - but, while perfectly legal (and in fact the simplest
possible implementation of the state pattern in Python), it may be
somewhat risky.
(snip)
> def write(self, str):
> self.seek(0, 2) # find end of file
> if((self.tell() + len(str)) >= self.capacity):
> print "changing type"
> flo = tempfile.TemporaryFile()
> flo.write(self.getvalue())
> self = flo
> print type(self)
Same comment here.
(snip)
Now for a practical solution : what you want is the strategy pattern.
from StringIO import StringIO
from tempfile import TemporaryFile
import sys
class TempFile(object):
"""A temporary file implementation that uses memory unless
either capacity is breached or fileno is requested, at which
point a real temporary file will be created and the relevant
details returned
"""
_strategies = (StringIO, TemporaryFile)
def __init__(self, buffer, capacity):
"""Creates a TempFile object containing the specified buffer.
If capacity is specified, we use a real temporary file once the
file gets larger than that size. Otherwise, the data is stored
in memory.
"""
self.capacity = capacity
self._delegate = self._strategies[len(buffer) > self.capacity]()
self.write(buffer)
def write(self, value):
print >> sys.stderr, \
"about to write %d more characters" % len(value)
if isinstance(self._delegate, self._strategies[0]):
len_value = len(value)
if len_value >= self.capacity:
needs_new_strategy = True
else:
self.seek(0, 2) # find end of file
needs_new_strategy = \
self.tell() + len_value >= self.capacity
if needs_new_strategy:
print >> sys.stderr, "changing strategy"
new_delegate = self._strategies[1]()
new_delegate.write(self.getvalue())
self._delegate = new_delegate
self._delegate.write(value)
def __getattr__(self, name):
# Takes care of automatic delegation,
# customize this according to your needs.
# Hint : this will only be called if normal lookup
# failed, so to control access to any _delegate's method,
# just implement a method with same name
try:
return getattr(self._delegate, name)
except AttributeError:
# hide the delegation
e = "object '%s' has no attribute '%s'" \
% (self.__class__.__name__, name)
raise AttributeError(e)
if __name__ == "__main__":
print "testing tempfile:"
tmp = TempFile("", 100)
ten_chars = "1234567890"
tmp.write(ten_chars * 5)
print "tmp < 100: ", tmp._delegate.__class__.__name__
tmp.write(ten_chars * 5)
print "tmp == 100: " , tmp._delegate.__class__.__name__
tmp.write("the last straw")
print "tmp > 100: " , tmp._delegate.__class__.__name__
More information about the Python-list
mailing list