46

For example:

>>> Spoken = namedtuple("Spoken", ["loudness", "pitch"])
>>> s = Spoken(loudness=90, pitch='high')
>>> str(s)
"Spoken(loudness=90, pitch='high')"

What I want is:

>>> str(s)
90

That is I want the string representation to display the loudness attribute. Is this possible ?

canadadry
  • 8,115
  • 12
  • 51
  • 68

3 Answers3

79

Yes, it is not hard to do and there is an example for it in the namedtuple docs.

The technique is to make a subclass that adds its own str method:

>>> from collections import namedtuple
>>> class Spoken(namedtuple("Spoken", ["loudness", "pitch"])):
        __slots__ = ()
        def __str__(self):
            return str(self.loudness)

>>> s = Spoken(loudness=90, pitch='high')
>>> str(s)
'90'

Update:

You can also used typing.NamedTuple to get the same effect.

from typing import NamedTuple

class Spoken(NamedTuple):
    
    loudness: int
    pitch: str
    
    def __str__(self):
        return str(self.loudness)
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • 15
    What's `__slots__` usage here? Is it required or optional? – clwen Jan 01 '14 at 19:20
  • 25
    Setting ``__slots__`` to an empty tuple is a memory saving technique. It makes sure that instances of ``Spoken`` take the same amount of memory as a regular tuple. Without the slots entry, every instance requires its own dictionary (which is much bigger than a tuple). – Raymond Hettinger Jan 30 '14 at 02:27
  • 2
    Besides the memory savings, it's worth noting that Python versions up to 3.5.0 [define](https://github.com/python/cpython/blob/v3.5.0/Lib/collections/__init__.py#L324) a `__dict__` property. Thus, the decision on whether to set `__slots__`, which controls the definition of the `__dict__` attribute, affects the behavior of functions that interact with that `__dict__` attribute; e.g. if `__slots__` isn't set, `vars(s)` returns `{}` rather than `OrderedDict([('loudness', 90), ('pitch', 'high')])`. This was changed in Python 3.5.1, as explained [here](https://stackoverflow.com/q/34166469/3903832). – Yoel Jul 08 '20 at 13:17
20

You can define a function for it:

def print_loudness(self):
    return str(self.loudness)

and assign it to __str__:

Spoken.__str__ = print_loudness
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • 1
    out of curiosity, will this return an `integer` or a `string`, since your overloaded `str` is returning an `int`? – Serdalis Oct 27 '11 at 09:53
  • 2
    @Serdalis: Well spotted, this will incorrectly return an `integer`. Fixing it now. – Björn Pollex Oct 27 '11 at 09:55
  • 2
    I like this way, as it adds little text to the default way of using ``namedtuple``. You could even shorten it using a lambda expression without losing much readability (IMHO): ``Spoken.__str__ = lambda x: str(x.loudness)``. (Of course you can use [``%`` or ``str.format(...)``](http://stackoverflow.com/q/5082452) inside the lambda expression). – djlauk Apr 04 '17 at 08:27
4

you can use code like this:

from collections import namedtuple

class SpokenTuple( namedtuple("Spoken", ["loudness", "pitch"]) ):

    def __str__(self):
        return str(self.loudness)

s = SpokenTuple(loudness=90, pitch='high')

print(str(s))

This will wrap namedtuple in a class of your choice which you then overload the str function too.

Serdalis
  • 10,296
  • 2
  • 38
  • 58