I had some problem printing user-defined class instance in containers. In short, if my code is:
class A():
def __str__(self):
return 'abc'
class B():
def __str__(self):
return str(A())
a,b=A(),B()
C=[[a],b]
print(C)
Then the output should be like:[[<__main__.A object at 0x02D99910>], <__main__.B object at 0x02DD5030>], but I want it to recursively apply customized __str__ and works even in nested lists or classes, i.e. I want the output to be [['abc'],'abc']. Any pythonic way to do?
#Blckknight should have submitted as an answer, because that seems to be the correct answer (it worked for my very similar question):
**override repr() instead of str()
def __repr__(self):
return "abc"
you can also add this for completeness
def str(self):
return self.repr()
#Blckknight if you want to resubmit as an answer for the points, you should. I wish I could just add a comment but i don't have the reputation to do that.
You would need to override the __str__ method of list, not A or B, since that's where the recursion needs to begin. That is, to turn a list into a string, you need to recursively turn each object in the list into a string. Unfortunately, you cannot do that. The best you can do is write a separate function, something like
def list_to_str(l):
if isinstance(l, list):
return "[%s]" % (", ".join(map(list_to_str, l)),)
else:
return "'%s'" % (str(l),)
print(list_to_str(C))
Related
This question already has answers here:
How to make print call the __str__ method of Python objects inside a list?
(8 answers)
Closed 4 years ago.
Coming from a Java background, I understand that __str__ is something like a Python version of toString (while I do realize that Python is the older language).
So, I have defined a little class along with an __str__ method as follows:
class Node:
def __init__(self, id):
self.id = id
self.neighbours = []
self.distance = 0
def __str__(self):
return str(self.id)
I then create a few instances of it:
uno = Node(1)
due = Node(2)
tri = Node(3)
qua = Node(4)
Now, the expected behaviour when trying to print one of these objects is that it's associated value gets printed. This also happens.
print uno
yields
1
But when I do the following:
uno.neighbours.append([[due, 4], [tri, 5]])
and then
print uno.neighbours
I get
[[[<__main__.Node instance at 0x00000000023A6C48>, 4], [<__main__.Node instance at 0x00000000023A6D08>, 5]]]
Where I expected
[[2, 4], [3, 5]]
What am I missing? And what otherwise cringe-worthy stuff am I doing? :)
Python has two different ways to convert an object to a string: str() and repr(). Printing an object uses str(); printing a list containing an object uses str() for the list itself, but the implementation of list.__str__() calls repr() for the individual items.
So you should also overwrite __repr__(). A simple
__repr__ = __str__
at the end of the class body will do the trick.
Because of the infinite superiority of Python over Java, Python has not one, but two toString operations.
One is __str__, the other is __repr__
__str__ will return a human readable string.
__repr__ will return an internal representation.
__repr__ can be invoked on an object by calling repr(obj) or by using backticks `obj`.
When printing lists as well as other container classes, the contained elements will be printed using __repr__.
It provides human readable version of output rather "Object": Example:
class Pet(object):
def __init__(self, name, species):
self.name = name
self.species = species
def getName(self):
return self.name
def getSpecies(self):
return self.species
def Norm(self):
return "%s is a %s" % (self.name, self.species)
if __name__=='__main__':
a = Pet("jax", "human")
print a
returns
<__main__.Pet object at 0x029E2F90>
while code with "str" return something different
class Pet(object):
def __init__(self, name, species):
self.name = name
self.species = species
def getName(self):
return self.name
def getSpecies(self):
return self.species
def __str__(self):
return "%s is a %s" % (self.name, self.species)
if __name__=='__main__':
a = Pet("jax", "human")
print a
returns:
jax is a human
Answer to the question
As pointed out in another answer and as you can read in PEP 3140, str on a list calls for each item __repr__. There is not much you can do about that part.
If you implement __repr__, you will get something more descriptive, but if implemented correctly, not exactly what you expected.
Proper implementation
The fast, but wrong solution is to alias __repr__ to __str__.
__repr__ should not be set to __str__ unconditionally. __repr__ should create a representation, that should look like a valid Python expression that could be used to recreate an object with the same value. In this case, this would rather be Node(2) than 2.
A proper implementation of __repr__ makes it possible to recreate the object. In this example, it should also contain the other significant members, like neighours and distance.
An incomplete example:
class Node:
def __init__(self, id, neighbours=[], distance=0):
self.id = id
self.neighbours = neighbours
self.distance = distance
def __str__(self):
return str(self.id)
def __repr__(self):
return "Node(id={0.id}, neighbours={0.neighbours!r}, distance={0.distance})".format(self)
# in an elaborate implementation, members that have the default
# value could be left out, but this would hide some information
uno = Node(1)
due = Node(2)
tri = Node(3)
qua = Node(4)
print uno
print str(uno)
print repr(uno)
uno.neighbours.append([[due, 4], [tri, 5]])
print uno
print uno.neighbours
print repr(uno)
Note: print repr(uno) together with a proper implementation of __eq__ and __ne__ or __cmp__ would allow to recreate the object and check for equality.
Well, container objects' __str__ methods will use repr on their contents, not str. So you could use __repr__ instead of __str__, seeing as you're using an ID as the result.
__str__ is only called when a string representation is required of an object.
For example str(uno), print "%s" % uno or print uno
However, there is another magic method called __repr__ this is the representation of an object. When you don't explicitly convert the object to a string, then the representation is used.
If you do this uno.neighbors.append([[str(due),4],[str(tri),5]]) it will do what you expect.
The thing about classes, and setting unencumbered global variables equal to some value within the class, is that what your global variable stores is actually the reference to the memory location the value is actually stored.
What you're seeing in your output is indicative of this.
Where you might be able to see the value and use print without issue on the initial global variables you used because of the str method and how print works, you won't be able to do this with lists, because what is stored in the elements within that list is just a reference to the memory location of the value -- read up on aliases, if you'd like to know more.
Additionally, when using lists and losing track of what is an alias and what is not, you might find you're changing the value of the original list element, if you change it in an alias list -- because again, when you set a list element equal to a list or element within a list, the new list only stores the reference to the memory location (it doesn't actually create new memory space specific to that new variable). This is where deepcopy comes in handy!
print self.id.__str__() would work for you, although not that useful for you.
Your __str__ method will be more useful when you say want to print out a grid or struct representation as your program develops.
print self._grid.__str__()
def __str__(self):
"""
Return a string representation of the grid for debugging.
"""
grid_str = ""
for row in range(self._rows):
grid_str += str( self._grid[row] )
grid_str += '\n'
return grid_str
I get a Runtime error (maximum recursion) when I execute the following code.
I'm trying to generate a list of instances; then I would like to print each one. I'm not sure what's going on here.
Anyway, what is the correct way to access each instance from the instance list?
I do realize I'm using a string of digits to name the instances and this is not cool. But say each number on the list is associated with a bunch of information. Then having attributes for each could make things accessible. I tried using a dict but I end up with nested dicts and I just didn't like it.
Thanks in advance.
class MyClass(object):
def __str__(self):
stuff= str(self)
return stuff
mylist = ['1234567','8910111','1213144','7654321']
inslist = [MyClass() for i in mylist]
print inslist[0]
The problem is in your __str__. If you call str(self), it will call itself. I think you meant was this:
class MyClass(object):
def __init__(self, i):
self.i = i
def __str__(self):
return str(self.i)
mylist = ['1234567','8910111','1213144','7654321']
inslist = [MyClass(i) for i in mylist]
print inslist[0]
I have a node object that has x and y variables. I keep all of the nodes in a list called nodelist. What is the most pythonic and simple way of printing coordinates of all nodes from nodelist?
More specifically, I have a class called someclass that has
someclass.state
someclass.nodelist
I want to print its state and coordinates of the nodes in nodelist in as few as possible lines.
something like: print self.state, (i.x, i.y for i in self.nodelist)
The pythonic way to print is to use print and the pythonic way to represent an object as a string (what would be printed) is to define __str__ (note that __unicode__ is gone in python3 and is maybe not that pythonic any longer, especially when __str__ will do (ie no unicode support is needed)). Note also that the new way of formatting string is with str.format rather than using the percent operator - so I use that.
In whatever class self is of:
def __str__(self):
return "{0} {1}".format(self.state, (str(i) for i in self.nodelist))
and in whatever class self.nodelist elements are in:
def __str__(self):
return "{0}, {1}".format(self.x, self.y)
and then just print the the objects using print(obj)
I want to implement a custom list class in Python as a subclass of list. What is the minimal set of methods I need to override from the base list class in order to get full type compatibility for all list operations?
This question suggest that at least __getslice__ needs to be overridden. From further research, also __add__ and __mul__ will be required. So I have this code:
class CustomList(list):
def __getslice__(self,i,j):
return CustomList(list.__getslice__(self, i, j))
def __add__(self,other):
return CustomList(list.__add__(self,other))
def __mul__(self,other):
return CustomList(list.__mul__(self,other))
The following statements work as desired, even without the overriding methods:
l = CustomList((1,2,3))
l.append(4)
l[0] = -1
l[0:2] = CustomList((10,11)) # type(l) is CustomList
These statements work only with the overriding methods in the above class definition:
l3 = l + CustomList((4,5,6)) # type(l3) is CustomList
l4 = 3*l # type(l4) is CustomList
l5 = l[0:2] # type(l5) is CustomList
The only thing I don't know how to achieve is making extended slicing return the right type:
l6 = l[0:2:2] # type(l6) is list
What do I need to add to my class definition in order to get CustomList as type of l6?
Also, are there other list operations other than extended slicing, where the result will be of list type instead of CustomList?
Firstly, I recommend you follow Björn Pollex's advice (+1).
To get past this particular problem (type(l2 + l3) == CustomList), you need to implement a custom __add__():
def __add__(self, rhs):
return CustomList(list.__add__(self, rhs))
And for extended slicing:
def __getitem__(self, item):
result = list.__getitem__(self, item)
try:
return CustomList(result)
except TypeError:
return result
I also recommend...
pydoc list
...at your command prompt. You'll see which methods list exposes and this will give you a good indication as to which ones you need to override.
You should probably read these two sections from the documentation:
Emulating container types
Additional methods for emulating sequence types (Python 2 only)
Edit: In order to handle extended slicing, you should make your __getitem__-method handle slice-objects (see here, a little further down).
Possible cut-the-gordian-knot solution: subclass UserList instead of list. (Worked for me.) That is what UserList is there for.
As a slight modification to Johnsywebs answer. I would only convert to a CustomList if item is a slice. Otherwise CustomList(["ab"])[0] would give you CustomList(["a", "b"]) which is not what you want. Like this:
def __getitem__(self, item):
result = list.__getitem__(self, item)
if type(item) is slice:
return CustomList(result)
else:
return result
This is probably a kinda commonly asked question but I could do with help on this. I have a list of class objects and I'm trying to figure out how to make it print an item from that class but rather than desplaying in the;
<__main__.evolutions instance at 0x01B8EA08>
but instead to show a selected attribute of a chosen object of the class. Can anyone help with that?
If you want to just display a particular attribute of each class instance, you can do
print([obj.attr for obj in my_list_of_objs])
Which will print out the attr attribute of each object in the list my_list_of_objs. Alternatively, you can define the __str__() method for your class, which specifies how to convert your objects into strings:
class evolutions:
def __str__(self):
# return string representation of self
print(my_list_of_objs) # each object is now printed out according to its __str__() method
You'll want to override your class's "to string" method:
class Foo:
def __str__(self):
return "String representation of me"
Checkout the __str__() and __repr__() methods.
See http://docs.python.org/reference/datamodel.html#object.__repr__
You need to override either the __str__, or __repr__ methods of your object[s]
My preference is to define a __repr__ function that can reconstruct the object (whenever possible). Unless you have a __str__ as well, both repr() and str() will call this method.
So for example
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return 'Foo(%r, %r)' % (self.a, self.b)
Doing it this way, you have a readable string version, and as a bonus it can be eval'ed to get a copy of the original object.
x = Foo(5, 1 + 1)
y = eval(str(x))
print y
-> Foo(5, 2)