Using namedtuples field names for column indices in a list of lists
python at deborahswanson.net
Mon Jan 9 23:27:38 EST 2017
MRAB wrote, on January 09, 2017 7:37 PM
> On 2017-01-10 03:02, Deborah Swanson wrote:
> > Erik wrote, on January 09, 2017 5:47 PM
> >> As people keep saying, the object you have called 'records' is a
> >> *list* of namedtuple objects. It is not a namedtuple.
> >> IIRC, you create it using a list comprehension which creates the
> >> records. A list comprehension always creates a list.
> > Well no. The list is created with:
> > records.extend(Record._make(row) for row in rows)
> > I'm new to both namedtuples and list comprehensions, so I'm not
> > exactly sure if this statement is a list comprehension. It
> looks like
> > it could be.
> This is a list comprehension:
> [Record._make(row) for row in rows]
> and this is a generator expression:
> (Record._make(row) for row in rows)
> It needs the outer parentheses.
> The .extend method will accept any iterable, including list
> records.extend([Record._make(row) for row in rows])
> and generator expressions:
> records.extend((Record._make(row) for row in rows))
> In the latter case, the generator expression is the only
> argument of the
> .extend method, and Python lets us drop the pair of parentheses:
> records.extend(Record._make(row) for row in rows)
> If there were another argument, it would be ambiguous and
> Python would
Appreciate your explanation of why this statement looks like a list
comprehension, but it isn't one.
> > In any case I recreated records in IDLE and got
> >>>> type(records)
> > <class 'list'>
> > So it's a class, derived from list? (Not sure what the
> 'list' means.)
[1, 2, 3]
So it is a list, despite not being made by a list comprehension and
despite its non-listlike behaviors. Guess I've never looked at the type
of a list before, probably because lists are so obvious by looking at
> > 'records' is in fact a class, it has an fget method and data members
> > that I've used. And it behaves like a list sometimes, but many times
> Its type is 'list', so it's an instance of a list, i.e. it's a list!
As testified by IDLE above! ;) A list of namedtuples may be an
instance of a list, but it doesn't always behave like a list of lists.
For example, if you want to modify an element of a record in records,
you can't just say
'record.Location = Tulsa'
like you can say
'record[Location] = Tulsa'
because each record is very much like a tuple, and tuples are immutable.
You have to use the _replace function:
record._replace(Location) = Tulsa
This is very unlike a list of lists. Only the outer data structure is a
list, and inside it's all namedtuples.
So it's not a list of lists, it's a list of namedtuples. But .sort and
sorted() DTRT, and that's valuable.
> > The only reason I've hedged away from advice to treat records as a
> > list for sorting until I tried it for myself, was because of an
> > lot of strange behavior I've seen, while trying to do the same
> > with namedtuples as I routinely do with scalars and lists. This is
> > new, and until now, unexplored territory for me. And I generally
> > saying I'm sure or outright agreeing with something unless I really
> > know it.
> >> The sorted() function and the list.sort() method can be used to
> >> a list containing any objects - it's just a case of telling them
> >> to obtain the key values to compare (which, in the case of
> >> simple attribute access which the namedtuple objects allow,
> >> "operator.attrgetter()" will
> >> do that). This is why sorting the list works for you.
> >> You could sort objects of different types - but you might need to
> >> supply a function instead of operator.attrgetter() which looks at
> >> the type of
> >> each object and returns something that's obtained differently
> >> for each
> >> type (but which the sort function can compare).
> >> When you say 'Foo = namedtuple("Foo", "spam ham")', you
> are creating
> >> a "factory" which is able to generate "Foo" objects for you.
> >> When you say "x = Foo(1, 2)" you are using the factory to
> create an
> >> object for you which has its "spam" and "ham" attributes
> set to the
> >> values 1 and 2 respectively.
> >> When you say "records = [Foo(x, y) for x, y in
> some_iterable()]", you
> >> are creating a list of such objects. This is the thing you
> are then
> >> sorting.
> >> Does that make sense?
> >> Regards, E.
> > Perfect sense. And now that I've confirmed in code that both
> > .sort() behave as hoped for with namedtuples, I couldn't be happier.
> > ;)
> > The only thing I don't think you have 100% correct is your assertion
> > that records is a list. And I'm really not sure now that
> > records.extend(Record._make(row) for row in rows)
> > is a list comprehension.
> > That's the last statement in the creation of 'records', and
> > immediately after that statement executes, the type function says
> > resulting 'records' is a class, probably derived from list, but it's
> > not a straight up list.
> > 'records' is enough different that you can't assume across the board
> > that namedtuples created this way are equivalent to a list. You do
> > into problems if you assume it behaves like a list, or even like
> > standard tuples, because it doesn't always. Believe me, when I first
> > started working with namedtuples, I got plenty snarled up debugging
> > code that was written assuming list behavior to know that a
> > of namedtuples is not exactly a list. Or even exactly like a list.
> > But that's just a quibble. The important thing in this context is
> > both .sort() and sorted() treat it like a list and DTRT.
> And that's very nice. ;)
> The list class has the .sort method, which sorts in-place. The
> function is a simple function that takes an iterable, iterates over it
> to build a list, sorts that list in-place, and then returns the list.
> The oft-stated rule is that not every 2- or 3-line function needs to
> a built-in, but 'sorted' is one of those cases where it's just nice to
> have it, a case of "practicality beats purity".
More information about the Python-list