44
from collections import namedtuple

Point = namedtuple('whatsmypurpose',['x','y'])
p = Point(11,22)
print(p)

Output:

whatsmypurpose(x=11,y=22)

What's the relevance/use of 'whatsmypurpose'?

wim
  • 338,267
  • 99
  • 616
  • 750
Phoenix
  • 4,386
  • 10
  • 40
  • 55
  • 2
    Check here: http://stackoverflow.com/questions/2970608/what-are-named-tuples-in-python "It becames more readable" - so, readability it is. And more: "you should use named tuples instead of tuples anywhere you think object notation will make your code more pythonic and more easily readable." – Raul Guiu Mar 31 '14 at 13:05
  • 1
    possible duplicate of [Use of class typenames in python](http://stackoverflow.com/questions/10482512/use-of-class-typenames-in-python) – b4hand Dec 05 '14 at 01:24

3 Answers3

12

namedtuple() is a factory function for tuple subclasses. Here, 'whatsmypurpose'is the type name. When you create a named tuple, a class with this name (whatsmypurpose) gets created internally.

You can notice this by using the verbose argument like:

Point=namedtuple('whatsmypurpose',['x','y'], verbose=True)

Also you can try type(p) to verify this.

martineau
  • 119,623
  • 25
  • 170
  • 301
haraprasadj
  • 1,059
  • 1
  • 8
  • 17
  • 3.3 also adds the attribute `_source`, in case you want to `print` or `exec` the definition. – Eryk Sun Mar 31 '14 at 22:22
  • I've don't seem to be able to access the class. ie, whatsmypurpose() above is not defined. Is the class actually defined somewhere or is typename only for display purposes? – Gadi Nov 02 '15 at 20:52
  • @Gadi Point is the reference (variable) to the class called 'whatsmypurpose' in the example above. So in general the class name (`Point.__name__`) is for display purposes (try `print(Point(1, 2))`). – zvyn Mar 29 '17 at 04:54
  • 1
    I think an important point to make here is that _**Python cannot surmise what your new class's name should be by looking at the name of the variable you're assigning the class object to**_. The variable doesn't't exist yet! Thus, Python needs you to explicitly tell it what name to assign the new class object. – Christian Dean May 14 '18 at 23:57
  • @ChristianDean Conceivably the redundancy could removed from *other end* by eliminating the need to explicitly assign `namedtuple(...)` to a variable. For example, I could imagine something like `register_namedtuple('name', ..., locals())` that automatically adds `name` to `locals()`. – jamesdlin Jun 28 '21 at 09:46
  • True @jamesdlin, although I'm not sure how I'd feel about a function hacking into local variables like that behind the scene. [Explict is better than implicit, no?](https://www.python.org/dev/peps/pep-0020/). – Christian Dean Jun 28 '21 at 09:49
9

'whatsmypurpose' gives the new subclass its type name. From the docs:

collections.namedtuple(typename, field_names, verbose=False,rename=False)
Returns a new tuple subclass named typename.

Here is an example:

>>> from collections import namedtuple
>>> Foo = namedtuple('Foo', ['a', 'b'])
>>> type(Foo)
<class 'type'>
>>> a = Foo(a = 1, b = 2)
>>> a
Foo(a=1, b=2)
>>> Foo = namedtuple('whatsmypurpose', ['a', 'b'])
>>> a = Foo(a = 1, b = 2)
>>> a
whatsmypurpose(a=1, b=2)
>>> 

Set the verbose parameter to True and you can see the complete whatsmypurpose class definition.

>>> Foo = namedtuple('whatsmypurpose', ['a', 'b'], verbose=True)
from builtins import property as _property, tuple as _tuple
from operator import itemgetter as _itemgetter
from collections import OrderedDict

class whatsmypurpose(tuple):
    'whatsmypurpose(a, b)'

    __slots__ = ()

    _fields = ('a', 'b')

    def __new__(_cls, a, b):
        'Create new instance of whatsmypurpose(a, b)'
        return _tuple.__new__(_cls, (a, b))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new whatsmypurpose object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 2:
            raise TypeError('Expected 2 arguments, got %d' % len(result))
        return result

    def _replace(_self, **kwds):
        'Return a new whatsmypurpose object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, ('a', 'b'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % list(kwds))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        return self.__class__.__name__ + '(a=%r, b=%r)' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self._fields, self))

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    a = _property(_itemgetter(0), doc='Alias for field number 0')

    b = _property(_itemgetter(1), doc='Alias for field number 1')
user2165
  • 1,951
  • 3
  • 20
  • 39
wwii
  • 23,232
  • 7
  • 37
  • 77
3

Consider:

class MyClass(tuple):
   pass

This creates a type, which is a tuple subclass, and it has a name MyClass.__name__ == "MyClass". namedtuple is a type factory, it also creates tuple subclasses, but in this functional API you have to pass the name explicitly.

When you assign the returned type to a different name:

Point = namedtuple('whatsmypurpose',['x','y'])

It is similar to doing this:

class whatsmypurpose(tuple):
    ... # extra stuff here to setup slots, field names, etc

Point = whatsmypurpose
del whatsmypurpose

In both cases you are just aliasing a different name to the type.

Usually you would assign to the same name as used for the type name. If you are bothered that repeating the same string is not DRY, then you can use the declarative API in typing.NamedTuple instead of that functional API in collections. Then you can be bothered by needing to annotate types, instead.

wim
  • 338,267
  • 99
  • 616
  • 750