Skip to content

Commit 704cfeb

Browse files
PEP 769: Incorporated open issues resolutions to the rest of the document. (#4214)
Incorporated open issues resolutions to the rest of the document.
1 parent bfd4b02 commit 704cfeb

File tree

1 file changed

+63
-65
lines changed

1 file changed

+63
-65
lines changed

peps/pep-0769.rst

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PEP: 769
2-
Title: Add a 'default' keyword argument to 'attrgetter' and 'itemgetter'
2+
Title: Add a 'default' keyword argument to 'attrgetter', 'itemgetter' and 'getitem'
33
Author: Facundo Batista <[email protected]>
44
Discussions-To: https://discuss.python.org/t/76419
55
Status: Draft
@@ -12,8 +12,8 @@ Abstract
1212
========
1313

1414
This 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
1717
specified default value when the targeted attribute or item is missing,
1818
thereby preventing exceptions and simplifying code that handles optional
1919
attributes or items.
@@ -31,6 +31,10 @@ Introducing a ``default`` parameter would streamline operations involving
3131
optional attributes or items, reducing boilerplate code and enhancing
3232
code 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

3539
Rationale
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

6877
Examples 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

149183
About Possible Implementations
150184
------------------------------
@@ -163,37 +197,43 @@ be impossible to distinguish what it returned on each step when an
163197
attribute 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

193233
Corner 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
197237
item/attribute would fail in the normal case. In other words, the
198238
object accessed should not handle defaults itself.
199239

@@ -255,8 +295,8 @@ combinations of ``attrgetter`` and ``itemgetter``, e.g.::
255295
(1, 2)
256296

257297
However, 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

261301
In the end, having multiple default values was deemed overly complex and
262302
potentially confusing, and a single ``default`` parameter was favored for
@@ -286,49 +326,7 @@ PEP.
286326
Open 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

334332
How to Teach This

0 commit comments

Comments
 (0)