[Python-ideas] Optional kwarg making attrgetter & itemgetter always return a tuple

Steven D'Aprano steve at pearwood.info
Fri Sep 14 03:20:38 CEST 2012

On 13/09/12 23:15, Masklinn wrote:
> attrgetter and itemgetter are both very useful functions, but both have
> a significant pitfall if the arguments passed in are validated but not
> controlled: if receiving the arguments (list of attributes, keys or
> indexes) from an external source and *-applying it, if the external
> source passes a sequence of one element both functions will in turn
> return an element rather than a singleton (1-element tuple).

For those who, like me, had to read this three or four times to work out
what Masklinn is talking about, I think he is referring to the fact that
attrgetter and itemgetter both return a single element if passed a single
index, otherwise they return a tuple of results.

If a call itemgetter(*args)(some_list) returns a tuple, was that tuple
a single element (and args contained a single index) or was the tuple
a collection of individual elements (and args contained multiple

py> itemgetter(*[1])(['a', ('b', 'c'), 'd'])
('b', 'c')
py> itemgetter(*[1, 2])(['a', 'b', 'c', 'd'])
('b', 'c')

> This means such code, for instance code "slicing" a matrix of some sort
> to get only some columns and getting the slicing information from its
> caller (in situation where extracting a single column may be perfectly
> sensible) will have to implement a manual dispatch between a "manual"
> getitem (or getattr) and an itemgetter (resp. attrgetter) call, e.g.
>      slicer = (operator.itemgetter(*indices) if len(indices)>  1
>                else lambda ar: [ar[indices[0]])

Why is this a problem? If you don't like writing this out in place, write
it once in a helper function. Not every short code snippet needs to be in
the standard library.

> This makes for more verbose and less straightforward code, I think it
> would be useful to such situations if attrgetter and itemgetter could be
> forced into always returning a tuple by way of an optional argument:


There is no need to add extra complexity to itemgetter and attrgetter for
something best solved in your code. Write a helper:

def slicer(*indexes):
     getter = itemgetter(*indexes)
     if len(indexes) == 1:
         return lambda seq: (getter(seq), )  # Wrap in a tuple.
     return getter


More information about the Python-ideas mailing list