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 > comprehensions: > > 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 > complain.
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] [1, 2, 3] >>> type(_) <class 'list'> 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 them. > > '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 not. > > > 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 awful > > lot of strange behavior I've seen, while trying to do the same things > > with namedtuples as I routinely do with scalars and lists. This is all > > new, and until now, unexplored territory for me. And I generally avoid > > saying I'm sure or outright agreeing with something unless I really do > > know it. > > > >> The sorted() function and the list.sort() method can be used to sort > >> a list containing any objects - it's just a case of telling them how > >> 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 sorted() and > > .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 the > > 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 run > > 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 namedtuple > > 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 that > > 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 'sorted' > 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 be > a built-in, but 'sorted' is one of those cases where it's just nice to > have it, a case of "practicality beats purity". > > -- > https://mail.python.org/mailman/listinfo/python-list > -- https://mail.python.org/mailman/listinfo/python-list