nltk/nltk — Issue #41: Definitions of __str__ and __repr__ do not follow the Python recommendation
Author: alexrudnick
Created: 2012-01-17 06:14:09+00:00
State: open

This is from the Python Language Reference:

* object.__repr__(self)
Called by the repr() built-in function and by string conversions (reverse quotes) to compute the “official” string representation of an object. If at all
possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate
environment). If this is not possible, a string of the form <...some useful description...> should be returned. The return value must be a string
object. If a class defines __repr__() but not __str__(), then __repr__() is also used when an “informal” string representation of instances of that class is
required.
This is typically used for debugging, so it is important that the representation is information-rich and unambiguous.

* object.__str__(self)
Called by the str() built-in function and by the print statement to compute the “informal” string representation of an object. This differs from
__repr__() in that it does not have to be a valid Python expression: a more convenient or concise representation may be used instead. The return
value must be a string object.

(http://docs.python.org/reference/datamodel.html#object.__repr__)

-----------------------------------------

But this isn't followed in NLTK. E.g., in grammar.Nonterminal __repr__ and __str__ are the same:

if isinstance(self._symbol, basestring):
return '%s' % (self._symbol,)
else:
return '%r' % (self._symbol,)

The same for grammar.Production, __repr__ calls __str__, and __str__ checks if the symbols in the LHS are Nonterminals (otherwise repr is called on
the symbol)

tree.Tree has a "correct" implementation of __repr__, but __str__ calls repr on the node if the node is not a string

chart.TreeEdge and chart.LeafEdge both call repr in the defintion of __str__, and __repr__ is just "[Edge: %s]", which is not a "valid Python expression
that could be used to recreate an object".

I haven't looked at more files than that, but I guess there's more

------------------------------------------

The problem with all this (apart from not following the Python standard) is if you want to use the classes for something else. E.g., I wanted to use
trees where the nodes are NOT strings or nonterminals. Suppose that I want to build a tree with dates:

>>> from datetime import date
>>> from nltk.tree import Tree
>>> xmas = date(2008, 12, 25)
>>> xmastree = Tree(xmas, ['jingle', 'jangle'])
>>> xmas
datetime.date(2008, 12, 25)
>>> xmastree
Tree(datetime.date(2008, 12, 25), ['jingle', 'jangle'])
>>> print xmas
2008-12-25
>>> print xmastree
(datetime.date(2008, 12, 25) jingle jangle)

I.e., str(xmastree) doesn't call str(xmas) when printing the node, but instead uses repr(xmas), which doesn't look good.

Migrated from http://code.google.com/p/nltk/issues/detail?id=154

earlier comments

peter.ljunglof@heatherleaf.se said, at 2008-12-17T08:09:48.000Z:

Perhaps my original example is better. I created a new class Focus, for annotating trees with focused nodes:

class Focus(object):
def __init__(self, focus):
self._focus = focus
def focus(self):
return self._focus
def __str__(self):
return '*%s*' % self._focus
def __repr__(self):
return 'Focus(%r)' % self._focus

Then we get the following:

>>> b = Focus('b')
>>> t = Tree('a', [Tree(b, ['c','d']), 'e'])
>>> b
Focus('b')
>>> t
Tree('a', [Tree(Focus('b'), ['c', 'd']), 'e'])
>>> print b
*b*
>>> print t
(a (Focus('b') c d) e)

But I want (a (*b* c d) e) in the last line.

StevenBird1 said, at 2008-12-18T00:28:35.000Z:

Thanks for this.

peter.ljunglof@heatherleaf.se said, at 2009-03-16T13:16:16.000Z:

Apparently Python has the same strange behaviour on containers, and this will probably NOT change:

http://www.python.org/dev/peps/pep-3140/