This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Patch for adding "default" to itemgetter and attrgetter
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 2.7
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: rhettinger, tebeka
Priority: low Keywords: patch

Created on 2008-10-14 22:07 by tebeka, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
getter.patch tebeka, 2008-10-14 22:07 Code path
Messages (7)
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) * (Python committer) Date: 2009-01-30 22:22
Will take a look at it next week.
msg80840 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) 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) * (Python committer) 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) * (Python committer) 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.
History
Date User Action Args
2022-04-11 14:56:40adminsetgithub: 48374
2009-04-01 21:14:25rhettingersetstatus: open -> closed
resolution: rejected
messages: + msg85096
2009-02-04 20:13:58rhettingersetpriority: low
2009-02-03 20:37:48tebekasetmessages: + msg81096
2009-02-03 20:28:51rhettingersetmessages: + msg81095
2009-02-03 06:47:21tebekasetmessages: + msg81028
2009-01-30 22:50:36rhettingersetmessages: + msg80840
2009-01-30 22:22:19rhettingersetassignee: rhettinger
type: enhancement
messages: + msg80839
nosy: + rhettinger
2008-10-14 22:07:33tebekacreate