11PEP: 769
2- Title: Add a 'default' keyword argument to 'attrgetter'  and 'itemgetter '
2+ Title: Add a 'default' keyword argument to 'attrgetter', 'itemgetter'  and 'getitem '
33Author: Facundo Batista <
[email protected] >
44Discussions-To: https://discuss.python.org/t/76419
55Status: Draft
@@ -12,8 +12,8 @@ Abstract
1212======== 
1313
1414This proposal aims to enhance the :mod: `operator ` module by adding a
15- ``default `` keyword argument to the ``attrgetter `` and  ``itemgetter ``
16- functions. This addition would allow these functions to return a
15+ ``default `` keyword argument to the ``attrgetter ``,  ``itemgetter `` and 
16+ `` getitem ``  functions. This addition would allow these functions to return a
1717specified default value when the targeted attribute or item is missing,
1818thereby preventing exceptions and simplifying code that handles optional
1919attributes or items.
@@ -31,6 +31,10 @@ Introducing a ``default`` parameter would streamline operations involving
3131optional attributes or items, reducing boilerplate code and enhancing
3232code clarity.
3333
34+ A similar situation occurs with ``getitem ``, with the added nuance that
35+ allowing the specification of a default value in this case would resolve
36+ a longstanding asymmetry with the :func: `getattr ` built-in function.
37+ 
3438
3539Rationale
3640========= 
@@ -58,11 +62,16 @@ Proposed behaviors:
5862- **itemgetter **: ``f = itemgetter(2, default=XYZ) `` followed by
5963  ``f(obj) `` would return ``obj[2] `` if that is valid, else ``XYZ ``.
6064
61- This enhancement applies to single and multiple attribute/item
62- retrievals, with the default value returned for any missing attribute or
63- item.
65+ - **getitem **: ``getitem(obj, k, XYZ) `` or
66+   ``getitem(obj, k, default=XYZ) `` would return ``obj[k] `` if that is valid,
67+   else ``XYZ ``.
68+ 
69+ In the first two cases, the enhancement applies to single and multiple
70+ attribute/item retrievals, with the default value returned for any
71+ missing attribute or item.
6472
65- No functionality change is incorporated if ``default `` is not used.
73+ No functionality change is incorporated in any case if the extra
74+ default (keyword) argument is not used.
6675
6776
6877Examples for attrgetter
@@ -144,7 +153,32 @@ With this PEP, using the proposed ``default`` keyword::
144153    ('bar', 'XYZ') 
145154
146155
147- .. _PEP 769 About Possible Implementations :
156+ Examples for getitem
157+ -------------------- 
158+ 
159+ The current behavior is unchanged::
160+ 
161+     >>> obj = ["foo", "bar", "baz"] 
162+     >>> getitem(obj, 1) 
163+     'bar' 
164+     >>> getitem(obj, 5) 
165+     Traceback (most recent call last): 
166+       File "<stdin>", line 1, in <module> 
167+     IndexError: list index out of range 
168+ 
169+ 
170+ With this PEP, using the proposed extra default, positionally or with
171+ a keyword::
172+ 
173+     >>> getitem(obj, 1, "XYZ") 
174+     'bar' 
175+     >>> getitem(obj, 5, "XYZ") 
176+     'XYZ' 
177+     >>> getitem(obj, 1, default="XYZ") 
178+     'bar' 
179+     >>> getitem(obj, 5, default="XYZ") 
180+     'XYZ' 
181+ 
148182
149183About Possible Implementations
150184------------------------------ 
@@ -163,37 +197,43 @@ be impossible to distinguish what it returned on each step when an
163197attribute chain is specified (e.g.
164198``attrgetter("foo.bar.baz", default=XYZ) ``).
165199
166- The implementation for ``itemgetter `` is not that easy. The more
167- straightforward way is also simple to define and
168- understand: attempting ``__getitem__ `` and catching a possible exception
169- (any of the three indicated in ``__getitem__ `` `reference `_). This way,
170- ``itemgetter(123, default=XYZ)(obj) `` would be equivalent to::
200+ The implementation for ``itemgetter `` and ``getitem `` is not that
201+ easy. The more straightforward way is also simple to define and
202+ understand: attempting ``__getitem__ `` and catching a possible
203+ exception (any of the three indicated in ``__getitem__ `` `reference `_).
204+ This way, ``itemgetter(123, default=XYZ)(obj) `` or
205+ ``getitem(obj, 123, default=XYZ) `` would be equivalent to::
171206
172207    try: 
173208        value = obj[123] 
174209    except (TypeError, IndexError, KeyError): 
175210        value = XYZ 
176211
177- However, this would be not as efficient as we'd want for certain cases,
178- e.g. using dictionaries where better performance is possible. A
179- more complex alternative would be::
212+ However, for performance reasons the implementation may look more
213+ like the following, which has the same exact behaviour::
180214
181-     if isinstance (obj,  dict) : 
215+     if type (obj) ==  dict: 
182216        value = obj.get(123, XYZ) 
183217    else: 
184218        try: 
185219            value = obj[123] 
186220        except (TypeError, IndexError, KeyError): 
187221            value = XYZ 
188222
189- While this provides better performance, it is more complicated to implement and explain. This is
190- the first case in the `Open Issues  <PEP 769 Open Issues _>`__ section later.
223+ Note how the verification is about the exact type and not using
224+ ``isinstance ``; this is to ensure the exact behaviour, which would be
225+ impossible if the object is a user defined one that inherits ``dict ``
226+ but overwrites ``get `` (similar reason to not check if the object has
227+ a ``get `` method).
228+ 
229+ This way, performance is better but it's just an implementation detail,
230+ so we can keep the original explanation on how it behaves.
191231
192232
193233Corner Cases
194234------------ 
195235
196- Providing a ``default `` option would only work when  accessing the
236+ Providing a ``default `` option would only work if  accessing the
197237item/attribute would fail in the normal case. In other words, the
198238object accessed should not handle defaults itself.
199239
@@ -255,8 +295,8 @@ combinations of ``attrgetter`` and ``itemgetter``, e.g.::
255295    (1, 2) 
256296
257297However, combining ``itemgetter `` or ``attrgetter `` is totally new
258- behavior and very complex to define.  While not impossible, it is beyond the scope of 
259- this PEP.
298+ behavior and very complex to define.  While not impossible, it is beyond
299+ the scope of  this PEP.
260300
261301In the end, having multiple default values was deemed overly complex and
262302potentially confusing, and a single ``default `` parameter was favored for
@@ -286,49 +326,7 @@ PEP.
286326Open Issues
287327=========== 
288328
289- Behavior Equivalence for ``itemgetter ``
290- --------------------------------------- 
291- 
292- For ``itemgetter ``, should it just attempt to
293- access the item and capture exceptions regardless of the object's API, or
294- should it first validate that the object provides a ``get `` method, and if so use it to
295- retrieve the item with a default? See examples in the `About Possible 
296- Implementations <PEP 769 About Possible Implementations_> `__ subsection
297- above.
298- 
299- This would help performance for the case of dictionaries, but would make
300- the ``default `` feature somewhat more difficult to explain, and a little
301- confusing if some object that is not a dictionary but still provides a ``get ``
302- method. Alternatively, we could call ``.get `` *only * if the
303- object is an instance of ``dict ``.
304- 
305- In any case, it is desirable that we do *not * affect performance
306- at all if the ``default `` is not triggered. Checking for ``.get `` would
307- be faster for dicts, but implies doing a verification
308- in all cases. Using the try/except model would make it less efficient as possible
309- in the case of dictionaries, but only if the
310- default is not triggered.
311- 
312- 
313- Add a Default to ``getitem ``
314- ---------------------------- 
315- 
316- It was proposed that we could also enhance ``getitem ``, as part of
317- this PEP, adding the ``default `` keyword to that function as well.
318- 
319- This will not only improve ``getitem `` itself, but we would also gain
320- internal consistency in the ``operator `` module and in comparison with
321- the ``getattr `` builtin function, which also has a default.
322- 
323- The definition could be as simple as the try/except proposed above, so
324- doing ``getitem(obj, name, default) `` would be equivalent to::
325- 
326-     try: 
327-         result = obj[name] 
328-     except (TypeError, IndexError, KeyError): 
329-         result = default 
330- 
331- (However see previous open issue about special case for dictionaries.)
329+ There are no open issues at this time.
332330
333331
334332How to Teach This
0 commit comments