[python] Confused about __str__ on list in Python

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? :)

This question is related to python string list printing tostring

The answer is


__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.


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

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.


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

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!


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.


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__.


Examples related to python

programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

Examples related to string

How to split a string in two and store it in a field String method cannot be found in a main class method Kotlin - How to correctly concatenate a String Replacing a character from a certain index Remove quotes from String in Python Detect whether a Python string is a number or a letter How does String substring work in Swift How does String.Index work in Swift swift 3.0 Data to String? How to parse JSON string in Typescript

Examples related to list

Convert List to Pandas Dataframe Column Python find elements in one list that are not in the other Sorting a list with stream.sorted() in Java Python Loop: List Index Out of Range How to combine two lists in R How do I multiply each element in a list by a number? Save a list to a .txt file The most efficient way to remove first N elements in a list? TypeError: list indices must be integers or slices, not str Parse JSON String into List<string>

Examples related to printing

How do I print colored output with Python 3? Print a div content using Jquery Python 3 print without parenthesis How to find integer array size in java Differences Between vbLf, vbCrLf & vbCr Constants Printing variables in Python 3.4 Show DataFrame as table in iPython Notebook Removing display of row names from data frame Javascript window.print() in chrome, closing new window or tab instead of cancelling print leaves javascript blocked in parent window Print a div using javascript in angularJS single page application

Examples related to tostring

How do I print my Java object without getting "SomeType@2f92e0f4"? What is the meaning of ToString("X2")? Printing out a linked list using toString ToString() function in Go to_string is not a member of std, says g++ (mingw) Confused about __str__ on list in Python How to convert an int array to String with toString method in Java Override valueof() and toString() in Java enum Checking session if empty or not Converting an object to a string