msg74774 - (view) |
Author: Miki Tebeka (tebeka) * |
Date: 2008-10-14 22:07 |
This is a patch for adding "default" keyword to itemgetter and attrgetter. This way you can do: >>> f = itemgetter(0, default=1) >>> f([]) 1 >>> f= attrgetter("a", default="b") >>> f(object()) 'b' >>> I'm not sure about all the Py_INCREF I've placed there, someone with more knowledge than me should review the code. |
|
|
msg80839 - (view) |
Author: Raymond Hettinger (rhettinger) *  |
Date: 2009-01-30 22:22 |
Will take a look at it next week. |
|
|
msg80840 - (view) |
Author: Raymond Hettinger (rhettinger) *  |
Date: 2009-01-30 22:50 |
Am curious about your use cases. ISTM that in every case I've every used either function, I've always known that the attribute or item is going to be there. For instance, the original motivating use cases were to support the key= argument to min/max/sorted/nlargest/nsmallest/groupby and to support iterator algebra with itertools: def powerset(iterable): '''Iterate over all subsets. >>> list(powerset('abc')) [set([]), set(['a']), set(['b']), set(['a', 'b'])] ''' # Only 1 initial call to PyObject_Hash(). # No trips around the eval-loop. # Memory friendly. seq = map(set, list(iterable)[::-1]) selector_stream = product([False, True], repeat=len(seq)) newsets, ns1 = tee(starmap(set, repeat(()))) components = imap(compress, repeat(seq), selector_stream) sets_and_components = imap(chain, izip(newsets), components) results = starmap(set.update, sets_and_components) return imap(itemgetter(0), izip(ns1, results)) |
|
|
msg81028 - (view) |
Author: Miki Tebeka (tebeka) * |
Date: 2009-02-03 06:47 |
Hmmm, too much time has passed and my 1bit memory has overflowed since :) IIRC it has to do with the fact that not all nodes in BeautifulSoup has a "name" attribute. I wanted to count how may "tr" there were, so len(filter(lambda n: n == "tr", map(attrgetter("name"), soup))) was what I wanted to do, but I got AttributeError. The natural way to me would be len(filter(lambda n: n == "tr", map(attrgetter("name", ""), soup))) but instead I had to do len(filter(lambda n: n == "tr", map(lambda n: gettattr(n, "name", ""), soup))) Another thing I can think of is for usage in count/max ... when some of the objects don't have the attribute you're looking for and it's by design (bad one maybe). You'd like to be able to do: max(map(itemgetter("time", 0)), articles) |
|
|
msg81095 - (view) |
Author: Raymond Hettinger (rhettinger) *  |
Date: 2009-02-03 20:28 |
That makes sense. You've found two object models that have optional attributes and have had some need to extract them with a default. My remaining concern is about adding complexity for functionality that is not often needed. It wouldn't be an issue if itemgetter() and attrgetter() already had a simple signature, but they already allow multiple arguments and IMO that doesn't mesh well with providing defaults. FWIW, looking back at your use cases, it feels like the functional tools have come together awkwardly. It may be slower, but the following seems easier to read, easier to write, and clearer about its intention: sum('tr' == getattr(node, 'name', '') for node in soup) max(getattr(art, 'time', 0) for art in articles) In general, listcomps and genexps read better than equivalents using lambda or a stack of builtin operators. And, lambda is dog slow. So, the following may be slower than the above code: len(filter(lambda n: n == "tr", map(attrgetter("name", ""), soup))) |
|
|
msg81096 - (view) |
Author: Miki Tebeka (tebeka) * |
Date: 2009-02-03 20:37 |
Can't we find a faster dog for lambda :) Anyway, I agree that we need to see more demand for that before going on. Let's keep this ticket open and see if someone else comes along. |
|
|
msg85096 - (view) |
Author: Raymond Hettinger (rhettinger) *  |
Date: 2009-04-01 21:14 |
Am closing this one because the API doesn't mesh well with the existing extensions for multiple attributes. If those weren't already present, there might be a case for adding this extension. |
|
|