an**************@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__