Question
This seems like it should be simple:
I want a list
like any other list
, except it has a different .__str__
method.
- Trying to set
object.__str__ = foo
results in a read-only error - Trying to subclass
list
means you need some way to convert an existinglist
to an instance of the subclass. This requires either copying all attributes manually (a huge pain), or somehow copying them all automatically, which I don't know how to do. - Trying to write a wrapper around the
list
object means I have to figure out some way to send all messages to the wrapped object except.__str__
which I handle with my own method. Don't know how to do this.
Any alternatives, or solutions #2 or #3 greatly appreciated. Thanks!
Solution
This solution works without a wrapper. And works if you join two lists by add. Any operation that modify the list itself will work as expected. Only functions that return a copy of the list like: sorted, reveresed will return the native python list which is fine. sort and reverse on the other hand operate on the list itself and will keep the type.
class myList(list):
def __new__(cls, data=None):
obj = super(myList, cls).__new__(cls, data)
return obj
def __str__(self):
return 'myList(%s)' % list(self)
def __add__(self, other):
return myList(list(self) + list(other))
>>> l = myList(range(5))
>>> print l
myList([0, 1, 2, 3, 4])
>>> print l + [1, 2]
myList([0, 1, 2, 3, 4, 1, 2])
>>> l.sort()
>>> print l
myList([0, 1, 2, 3, 4])
OTHER TIPS
If you would like to override __str__
for other containers (e.g., tuple
), you can take advantage of multiple inheritance:
class PrettyStr(object):
def __str__(self):
ret = ''
if isinstance(self, (list, tuple)):
ret = ''.join(str(elem) for elem in self)
else:
pass # handle other types here
return ret
class MyList(PrettyStr, list):
pass
class MyTuple(PrettyStr, tuple):
pass
if __name__ == "__main__":
print MyList([1, 2, 3, 4])
print MyTuple((1, 2, 3, 4))
You could extend the list class and override it:
class myList(list):
def __str__(self):
# do something
return "something"
Edit: removed an incorrect part of the answer which suggested dynamically replacing __str__
on the list object, which is not allowed in the implementation of Python lists.
I'm a Java programmer but I think that is what you want (tested with python 2.6):
>>> class myList(list):
... def __str__(self):
... return "aaa"
...
>>> def myListWrapper(list):
... return myList(list)
...
>>> a = [1, 2, 3]
>>> type(a)
<type 'list'>
>>> b = myListWrapper(a)
>>> type(b)
<class '__main__.myList'>
>>> print(a)
[1, 2, 3]
>>> print(b)
aaa
class MyList(list):
def __str__(self):
return "foo"
str(MyList([1, 2, 3]))
'foo'
str(MyList(list([1, 2, 3])))
'foo'
My earlier comments as an answer. As you can see MyList accepts any sequence in its constructor.
Which raises the question: why do you want to override the __str__ method?
Wouldnt it be better to create a class to encapsulate your object?
class MyContainer(object):
def __init__(self, list):
self.containedList = list
def __str__(self):
print('All hail Python')
This way you don't have to worry about converting your object, or copying the attributes, or whatsoever. (by the way, how expensive is MyList(longlist)? Is it an intelligent copy, or a dumb "let's recreate a list object from an iterable?")
If, at some point, it looks complicated to do what you're trying to do, it might mean that you're doing it wrong :p
How about wrapping the list?
>>> class StrList(object):
def __init__(self, data=None):
self._data = data or []
def __str__(self):
return "StrList!"
def __getattr__(self, attr):
if attr == "_data":
return self.__dict__[attr]
return getattr(self._data, attr)
def __setattr__(self, key, val):
if key == "_data":
self.__dict__[key] = val
else:
setattr(self._data, key, val)
def __getitem__(self, index):
return self._data[index]
def __setitem__(self, index, value):
self._data[index] = value
>>> l = StrList(range(3))
>>> print l
StrList!
>>> l[0]
0
>>> for x in l:
print x
0
1
2
>>> l[0] = "spam"
>>> l.append("egg")
>>> print list(l)
['spam', 1, 2, 'egg']
>>>
Ex = ["a", 2, 4] #our_list
Ex(-1) = "xuxu_beleza" #the_name_of_your_list(index) = new_item
Print(Ex)
["a", 2, "xuxu_beleza"] #our_new_list
**list[index] = new_item**