I have a class TextBuffer
. It has an __init__
function which can be used to pass an initial value for the content of TextBuffer
. If no value is passed, then it should use an empty list as default value.
The class has a method add, in order to add text to the TextBuffer:
a = TextBuffer()
a += "TEXT"
For some reason, calling the __init__
method without parameters does not use the default value of []
for lst
, but a previously added value to another instance of TextBuffer
, which is not in the __init__
call
a = TextBuffer()
a += "BUFFER A"
b = TextBuffer() ### lst in the __init__ call has the value of ["BUFFER A"]
b += "BUFFER B"
I have no idea why this happens. Maybe I'm doing something wrong in the __add__
method?
Please consider the complete example code:
import pprint
### Two functions for byte/string conversion in Python3
def b(x):
if ( type(x) == type(b'')):
return x
else:
return x.encode(encoding='UTF-8')
def s(x):
if ( type(x) == type(b'') ):
return x.decode(encoding='UTF-8')
else:
return x
class TextBuffer():
def __init__( self, lst = [], maxlines = None ):
self.content = []
if ( type(lst) == type([])):
if (len(lst) > 0):
print("INIT got a nonempty lst value:")
pprint.pprint(lst)
print("------------------------------")
self.content = lst
def __getitem__( self, i ):
try:
return self.content[i]
except IndexError:
return None
def __iter__( self ):
for line in self.content:
yield line
def __contains__( self, item ):
return item in self.content
def __len__( self ):
return len(self.content)
def __add__( self, item ):
self.content.append(item)
return self
def __radd__( self, item ):
return self.__add__(item)
def __repr__( self ):
result = ""
for line in self.content:
if ( type(line) == type(b"") ):
result+=s(line)
else:
result+=line
return result
def __str__( self ):
return repr(self)
def __unicode__( self ):
return repr(self.content)
### TextBuffer INIT with empty list creates an empty textbuffer
a = TextBuffer(lst=[])
print("a = TextBuffer(lst=[])")
a += "BUFFER A"
### TextBuffer INIT with empty list creates an empty textbuffer
b = TextBuffer(lst=[])
print("b = TextBuffer(lst=[])")
b += "BUFFER B"
print("Content of TextBuffer a:")
print(a)
print("Content of TextBuffer b:")
print(b)
print("-------------------------")
### TextBuffer INIT without any parameters should use default value for lst of []
### So an empty list. Should be the same as TextBuffer(lst=[])
a = TextBuffer()
print("a = TextBuffer()")
a += "BUFFER A"
### TextBuffer INIT without any parameters should use default value for lst of []
### So an empty list. Should be the same as TextBuffer(lst=[])
### But now, the value of lst is not [], it is the string added to TextBuffer 'a': ['BUFFER A']
b = TextBuffer()
print("b = TextBuffer()")
b += "BUFFER B"
print("Content of TextBuffer a:")
print(a)
print("Content of TextBuffer b:")
print(b)
You cannot use lst = []
as default argument in this case, since Python will makes one list in memory and passes each time a reference to the same object (list) to the constructor. This means that your two objects share the same lst
and thus modifications made by one object are reflected in the other and vice versa.
You can use the following trick:
def __init__( self, lst = None, maxlines = None ):
if lst is None:
lst = []
#... (remainder of the constructor)
since here you force Python to construct a new list. Evidently you could use another value than None
; but I think for your case this will be sufficient.
In general it is a good idea to not feed mutable objects as default values, always use immutable objects. There are however, as @hiroprotagonist describes cases where this behavior is wanted (like memoization), but I would advice to be very careful with these.