Python syntax for namedtuple

2019-02-23 03:29发布

问题:

I see that the Python syntax for a namedtuple is:

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

Why isn't it simpler like so:

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

Its less verbose,

回答1:

namedtuple is a factory, returning a class. Consider only expression:

namedtuple(['x','y'])

What would be the name of class returned by this expression?



回答2:

In general, objects don't know what variables they are assigned to:

# Create three variables referring to an OrderedPair class

tmp = namedtuple('OrderedPair', ['x','y'])  # create a new class with metadata
Point = tmp                                 # assign the class to a variable
Coordinate = tmp                            # assign the class to another var

That's a problem for named tuples. We have to pass in the class name to the namedtuple() factory function so that the class can be given a useful name, docstring, and __repr__ all of which have the class name inside it.

These reason it seems strange to you is that normal function and class definitions are handled differently. Python has special syntax for def and class that not only creates functions and classes, but it assigns their metadata (name and docstring) and assigns the result to a variable.

Consider what def does:

def square(x):
    'Return a value times itself'
    return x * x

The keyword def takes care of several things for you (notice that the word "square" will be used twice):

tmp = lambda x: x*x                         # create a function object
tmp.__name__ = 'square'                     # assign its metadata
tmp.__doc__ = 'Return a value times itself'
square = tmp                                # assign the function to a variable

The same is also true for classes. The class keyword takes care of multiple actions that would otherwise repeat the class name:

class Dog(object):
    def bark(self):
        return 'Woof!'

The underlying steps repeat the class name (notice that the word "Dog" is used twice):

Dog = type('Dog', (object,), {'bark': lambda self: 'Woof'})

Named tuples don't have the advantage of a special keyword like def or class so it has to do the first to steps itself. The final step of assigning to a variable belongs to you. If you think about it, the named tuple way is the norm in Python while def and class are the exception:

 survey_results = open('survey_results')      # is this really a duplication?
 company_db = sqlite3.connect('company.db')   # is this really a duplication?
 www_python_org = urllib.urlopen('http://www.python.org')
 radius = property(radius)

You are not the first to notice this. PEP 359 that suggested we add a new keyword, make, that could allow any callable to gain the auto-assignment capabilities of def, class, and import.

make <callable> <name> <tuple>:
    <block>

would be translated into the assignment:

<name> = <callable>("<name>", <tuple>, <namespace>)

In the end, Guido didn't like the "make" proposal because it caused more problems than it solved (after all, it only saves you from making a single variable assignment).

Hope that helps you see why the class name is written twice. It isn't really duplication. The string form of the class name is used to assign metadata when the object is created, and the separate variable assignment just gives you a way to refer to that object. While they are usually the same name, they don't have to be :-)



回答3:

The class should have a name and know it. And it doesn't see the variable you assign it to, so it can't use that. Plus you could call it something else or even nothing at all:

c = namedtuple('Point', ['x', 'y'])
do_something_with_this(namedtuple('Point', ['x', 'y']))

Speaking of simpler syntax, you can also write it like this:

namedtuple('Point', 'x y')


回答4:

Because namedtuple is a function that returns a class. To do that, it is actually rendering a string template and calling eval. To build the string, it needs all the arguments beforehand.

You need to include the relevant context as arguments to namedtuple for that to happen. If you don't provide the class name argument, it would need to guess. Programming languages don't like to guess.

With the rules of the Python language, the namedtuple function within this expression..

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

..doesn't have access to variable name (Point) that the result is stored in once the expression has been executed. It only has access to the elements of the list provided as its argument (and variables that have been defined earlier).