From fcdb6c4055f2d642df4b771a045fa997c0ba7032 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Sun, 22 Dec 2024 13:21:40 -0300 Subject: [PATCH 1/7] Add PEP about default in attrgetter and itemgetter. --- .github/CODEOWNERS | 1 + peps/pep-0790.rst | 272 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 peps/pep-0790.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a48e9dbc1b0..e5963d3945c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -650,6 +650,7 @@ peps/pep-0768.rst @pablogsal peps/pep-0777.rst @warsaw # ... peps/pep-0789.rst @njsmith +peps/pep-0790.rst @facundobatista # ... peps/pep-0801.rst @warsaw # ... diff --git a/peps/pep-0790.rst b/peps/pep-0790.rst new file mode 100644 index 00000000000..00e22875168 --- /dev/null +++ b/peps/pep-0790.rst @@ -0,0 +1,272 @@ +PEP: 790 +Title: Add a 'default' keyword argument to 'attrgetter' and 'itemgetter' +Author: Facundo Batista +Status: Draft +Type: Standards Track +Created: 22-12-2024 +Python-Version: 3.14 + + +Abstract +======== + +This proposal aims to enhance the ``operator`` module by adding a ``default`` keyword argument to the ``attrgetter`` and ``itemgetter`` functions. This addition would allow these functions to return a specified default value when the targeted attribute or item is missing, thereby preventing exceptions and simplifying code that handles optional attributes or items. + + +Motivation +========== + +Currently, `attrgetter` and `itemgetter` raise exceptions if the specified attribute or item is absent. This limitation requires developers to implement additional error handling, leading to more complex and less readable code. + +Introducing a `default` parameter would streamline operations involving optional attributes or items, reducing boilerplate code and enhancing code clarity. + + +Rationale +========= + +The primary design decision is to introduce a single `default` parameter applicable to all specified attributes or items. + +This approach maintains simplicity and avoids the complexity of assigning individual default values to multiple attributes or items. While some discussions considered allowing multiple defaults, the increased complexity and potential for confusion led to favoring a single default value for all cases (more about this below in Rejected Ideas). + + +Specification +============= + +Proposed behaviours: + +- **attrgetter**: ``f = attrgetter("name", default=XYZ)`` followed by ``f(obj)`` would return ``obj.name`` if the attribute exists, else ``XYZ``. + +- **itemgetter**: ``f = itemgetter(2, default=XYZ)`` followed by ``f(obj)`` would return ``obj[2]`` if that is valid, else ``XYZ``. + +This enhancement applies to single and multiple attribute/item retrievals, with the default value returned for any missing attribute or item. + +No functionality change is incorporated if ``default`` is not used. + + +Examples for attrgetter +----------------------- + +Current behaviour, no changes introduced:: + + >>> class C: + ... class D: + ... class X: + ... pass + ... class E: + ... pass + ... + >>> attrgetter("D")(C) + + >>> attrgetter("badname")(C) + Traceback (most recent call last): + File "", line 1, in + AttributeError: type object 'C' has no attribute 'badname' + >>> attrgetter("D", "E")(C) + (, ) + >>> attrgetter("D", "badname")(C) + Traceback (most recent call last): + File "", line 1, in + AttributeError: type object 'C' has no attribute 'badname' + >>> attrgetter("D.X")(C) + + >>> attrgetter("D.badname")(C) + Traceback (most recent call last): + File "", line 1, in + AttributeError: type object 'D' has no attribute 'badname' + +Using ``default``:: + + >>> attrgetter("D", default="noclass")(C) + + >>> attrgetter("badname", default="noclass")(C) + 'noclass' + >>> attrgetter("D", "E", default="noclass")(C) + (, ) + >>> attrgetter("D", "badname", default="noclass")(C) + (, 'noclass') + >>> attrgetter("D.X", default="noclass")(C) + + >>> attrgetter("D.badname", default="noclass")(C) + 'noclass' + + +Examples for itemgetter +----------------------- + +Current behaviour, no changes introduced:: + + >>> obj = ["foo", "bar", "baz"] + >>> itemgetter(1)(obj) + 'bar' + >>> itemgetter(5)(obj) + Traceback (most recent call last): + File "", line 1, in + IndexError: list index out of range + >>> itemgetter(1, 0)(obj) + ('bar', 'foo') + >>> itemgetter(1, 5)(obj) + Traceback (most recent call last): + File "", line 1, in + IndexError: list index out of range + + +Using ``default``:: + + >>> itemgetter(1, default="XYZ")(obj) + 'bar' + >>> itemgetter(5, default="XYZ")(obj) + 'XYZ' + >>> itemgetter(1, 0, default="XYZ")(obj) + ('bar', 'foo') + >>> itemgetter(1, 5, default="XYZ")(obj) + ('bar', 'XYZ') + + +About Posible Implementations +----------------------------- + +For the case of ``attrgetter`` is quite direct: it implies using ``getattr`` catching a possible ``AttributeError``. So ``attrgetter("name", default=XYZ)(obj)`` would be like: + + try: + value = getattr(obj, "name") + except (TypeError, IndexError, KeyError): + value = XYZ + +Note we cannot rely on using ``gettattr`` with a default value, as would be impossible to distinguish what it returned on each step when an attribute chain is specified (e.g. ``attrgetter("foo.bar.baz", default=XYZ)``). + +For the case of ``itemgetter`` it's not that easy. The more straightforward way is similar to above, also simple to define and understand: attempting ``__getitem__`` and catching a possible exception (any of the three indicated in ``__getitem__`` reference). This way, ``itemgetter(123, default=XYZ)(obj)`` would be equivalent to:: + + try: + value = obj[123] + except (TypeError, IndexError, KeyError): + value = XYZ + +However, this would be not as eficient as we'd want for particular cases, e.g. using dictionaries where particularly good performance is desired. A more complex alternative would be:: + + if isinstance(obj, dict): + value = obj.get(123, XYZ) + else: + try: + value = obj[123] + except (TypeError, IndexError, KeyError): + value = XYZ + +Better performance, more complicated to implement and explain. This is the first case in the Open Issues section later. + + +Corner Cases +------------ + +Providing a ``default`` option would only work when accessing to the item/attribute would fail in a regular situation. In other words, the object accessed should not handle defaults theirselves. + +For example, the following would be redundant/confusing because ``defaultdict`` will never error out when accessing the item:: + + >>> from collections import defaultdict + >>> from operator import itemgetter + >>> dd = defaultdict(int) + >>> itemgetter("foo", default=-1)(dd) + 0 + +The same applies to any user built object that overloads ``__getitem__`` or ``__getattr__`` implementing fallbacks. + + +Rejected Ideas +============== + +Multiple Default Values +----------------------- + +The idea of allowing multiple default values for multiple attributes or items was considered. + +Two alternatives were discussed, using an iterable that must have the same quantity of items than parameters given to ``attrgetter``/``itemgetter``, or using a dictionary with keys matching those names passed to ``attrgetter``/``itemgetter``. + +The really complex thing to solve in these casse, that would make the feature hard to explain and with confusing corners, is what would happen if an iterable or dictionary is the *unique* default desired for all items. For example:: + + >>> itemgetter("a", default=(1, 2)({}) + (1, 2) + >>> itemgetter("a", "b", default=(1, 2))({}) + ((1, 2), (1, 2)) + +If we allow "multiple default values" using ``default``, the first case in the example above would raise an exception because more items in the default than names, and the second case would return ``(1, 2))``. This is why emerged the possibility of using a different name for multiple defaults (``defaults``, which is expressive but maybe error prone because too similar to ``default``). + +As part of this conversation there was another proposal that would enable multiple defaults, which is allowing combinations of ``attrgetter`` and ``itemgetter``, e.g.:: + + >>> ig_a = itemgetter("a", default=1) + >>> ig_b = itemgetter("b", default=2) + >>> ig_combined = itemgetter(ig_a, ig_b) + >>> ig_combined({"a": 999}) + (999, 2) + >>> ig_combined({}) + (1, 2) + +However, combining ``itemgetter`` or ``attrgetter`` is a totally new behaviour very complex to define, not impossible, but beyond the scope of this PEP. + +At the end having multiple default values was deemed overly complex and potentially confusing, and a single ``default`` parameter was favored for simplicity and predictability. + + +Tuple Return Consistency +------------------------ + +Another rejected proposal was adding a a flag to always return tuple regardless of how many keys/names/indices were sourced to arguments. E.g.:: + + >>> letters = ["a", "b", "c"] + >>> itemgetter(1, return_tuple=True)(letters) + ('b',) + >>> itemgetter(1, 2, return_tuple=True)(letters) + ('b', 'c') + +This would be of a little help for multiple default values consistency, but requires further discussion and for sure is out of the scope of this PEP. + + +Open Issues +=========== + +Behaviour Equivalence for ``itemgetter`` +---------------------------------------- + +We need to define how ``itemgetter`` would behave, if just attempt to access the item and capture exceptions no matter which the object, or validate first if the object provides a ``get`` method and use it to retrieve the item with a default. See examples in the About Posible Implementations subsection before. + +This would help performance for the case of dictionaries, but would make the ``default`` feature somewhat more difficult to explain, and a little confusing if some object that is not a dictionary but provides a ``get`` method is used. Alternatively, we could call ``.get`` *only* if the object is an instance of ``dict``. + +In any case, a desirable situation is that we do *not* affect performance at all if the ``default`` is not triggered. Checking for ``.get`` would get the default faster in case of dicts, but implies doing a verification in all cases. Using the try/except model would make it not as fast as it could in the case of dictionaries, but would not introduce delays if the default is not triggered. + + +Add a Default to ``getitem`` +-------------------------- + +It was proposed that we could also enhance ``getitem``, as part of the of this PEP, adding ``default`` also to it. + +This will not only improve ``getitem`` itself, but we would also gain internal consistency in the ``operator`` module and in comparison with the ``getattr`` builtin function that also has a default. + +The definition could be as simple as the try/except proposed above, so doing ``getitem(obj, name, default)`` would be equivalent to:: + + try: + result = obj[name] + except (TypeError, IndexError, KeyError): + resutl = default + +(however see previous open issue about special case for dictionaries) + + +How to Teach This +================= + +As the basic behaviour is not modified, this new ``default`` can be avoided when teaching ``attrgetter`` and ``itemgetter`` for the first time, and can be introduced only when the functionality need arises. + + +Backwards Compatibility +======================= + +The proposed changes are backward-compatible. The ``default`` parameter is optional; existing code without this parameter will function as before. Only code that explicitly uses the new ``default`` parameter will exhibit the new behavior, ensuring no disruption to current implementations. + + +Security Implications +===================== + +Introducing a ``default`` parameter does not inherently introduce security vulnerabilities. + + +Copyright +========= + +This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. From 4e5667fb2eac65838424f2b1d13a849a230785e5 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Wed, 25 Dec 2024 10:53:24 -0300 Subject: [PATCH 2/7] Wrapped lines. --- peps/pep-0790.rst | 153 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 35 deletions(-) diff --git a/peps/pep-0790.rst b/peps/pep-0790.rst index 00e22875168..76491be8fd6 100644 --- a/peps/pep-0790.rst +++ b/peps/pep-0790.rst @@ -10,23 +10,38 @@ Python-Version: 3.14 Abstract ======== -This proposal aims to enhance the ``operator`` module by adding a ``default`` keyword argument to the ``attrgetter`` and ``itemgetter`` functions. This addition would allow these functions to return a specified default value when the targeted attribute or item is missing, thereby preventing exceptions and simplifying code that handles optional attributes or items. +This proposal aims to enhance the ``operator`` module by adding a +``default`` keyword argument to the ``attrgetter`` and ``itemgetter`` +functions. This addition would allow these functions to return a +specified default value when the targeted attribute or item is missing, +thereby preventing exceptions and simplifying code that handles optional +attributes or items. Motivation ========== -Currently, `attrgetter` and `itemgetter` raise exceptions if the specified attribute or item is absent. This limitation requires developers to implement additional error handling, leading to more complex and less readable code. +Currently, ``attrgetter`` and ``itemgetter`` raise exceptions if the +specified attribute or item is absent. This limitation requires +developers to implement additional error handling, leading to more +complex and less readable code. -Introducing a `default` parameter would streamline operations involving optional attributes or items, reducing boilerplate code and enhancing code clarity. +Introducing a ``default`` parameter would streamline operations involving +optional attributes or items, reducing boilerplate code and enhancing +code clarity. Rationale ========= -The primary design decision is to introduce a single `default` parameter applicable to all specified attributes or items. +The primary design decision is to introduce a single ``default`` parameter +applicable to all specified attributes or items. -This approach maintains simplicity and avoids the complexity of assigning individual default values to multiple attributes or items. While some discussions considered allowing multiple defaults, the increased complexity and potential for confusion led to favoring a single default value for all cases (more about this below in Rejected Ideas). +This approach maintains simplicity and avoids the complexity of assigning +individual default values to multiple attributes or items. While some +discussions considered allowing multiple defaults, the increased +complexity and potential for confusion led to favoring a single default +value for all cases (more about this below in Rejected Ideas). Specification @@ -34,11 +49,16 @@ Specification Proposed behaviours: -- **attrgetter**: ``f = attrgetter("name", default=XYZ)`` followed by ``f(obj)`` would return ``obj.name`` if the attribute exists, else ``XYZ``. +- **attrgetter**: ``f = attrgetter("name", default=XYZ)`` followed by + ``f(obj)`` would return ``obj.name`` if the attribute exists, else + ``XYZ``. -- **itemgetter**: ``f = itemgetter(2, default=XYZ)`` followed by ``f(obj)`` would return ``obj[2]`` if that is valid, else ``XYZ``. +- **itemgetter**: ``f = itemgetter(2, default=XYZ)`` followed by + ``f(obj)`` would return ``obj[2]`` if that is valid, else ``XYZ``. -This enhancement applies to single and multiple attribute/item retrievals, with the default value returned for any missing attribute or item. +This enhancement applies to single and multiple attribute/item +retrievals, with the default value returned for any missing attribute or +item. No functionality change is incorporated if ``default`` is not used. @@ -125,23 +145,34 @@ Using ``default``:: About Posible Implementations ----------------------------- -For the case of ``attrgetter`` is quite direct: it implies using ``getattr`` catching a possible ``AttributeError``. So ``attrgetter("name", default=XYZ)(obj)`` would be like: +For the case of ``attrgetter`` is quite direct: it implies using +``getattr`` catching a possible ``AttributeError``. So +``attrgetter("name", default=XYZ)(obj)`` would be like: try: value = getattr(obj, "name") except (TypeError, IndexError, KeyError): value = XYZ -Note we cannot rely on using ``gettattr`` with a default value, as would be impossible to distinguish what it returned on each step when an attribute chain is specified (e.g. ``attrgetter("foo.bar.baz", default=XYZ)``). +Note we cannot rely on using ``gettattr`` with a default value, as would +be impossible to distinguish what it returned on each step when an +attribute chain is specified (e.g. +``attrgetter("foo.bar.baz", default=XYZ)``). -For the case of ``itemgetter`` it's not that easy. The more straightforward way is similar to above, also simple to define and understand: attempting ``__getitem__`` and catching a possible exception (any of the three indicated in ``__getitem__`` reference). This way, ``itemgetter(123, default=XYZ)(obj)`` would be equivalent to:: +For the case of ``itemgetter`` it's not that easy. The more +straightforward way is similar to above, also simple to define and +understand: attempting ``__getitem__`` and catching a possible exception +(any of the three indicated in ``__getitem__`` reference). This way, +``itemgetter(123, default=XYZ)(obj)`` would be equivalent to:: try: value = obj[123] except (TypeError, IndexError, KeyError): value = XYZ -However, this would be not as eficient as we'd want for particular cases, e.g. using dictionaries where particularly good performance is desired. A more complex alternative would be:: +However, this would be not as eficient as we'd want for particular cases, +e.g. using dictionaries where particularly good performance is desired. A +more complex alternative would be:: if isinstance(obj, dict): value = obj.get(123, XYZ) @@ -151,15 +182,19 @@ However, this would be not as eficient as we'd want for particular cases, e.g. u except (TypeError, IndexError, KeyError): value = XYZ -Better performance, more complicated to implement and explain. This is the first case in the Open Issues section later. +Better performance, more complicated to implement and explain. This is +the first case in the Open Issues section later. Corner Cases ------------ -Providing a ``default`` option would only work when accessing to the item/attribute would fail in a regular situation. In other words, the object accessed should not handle defaults theirselves. +Providing a ``default`` option would only work when accessing to the +item/attribute would fail in a regular situation. In other words, the +object accessed should not handle defaults theirselves. -For example, the following would be redundant/confusing because ``defaultdict`` will never error out when accessing the item:: +For example, the following would be redundant/confusing because +``defaultdict`` will never error out when accessing the item:: >>> from collections import defaultdict >>> from operator import itemgetter @@ -167,7 +202,8 @@ For example, the following would be redundant/confusing because ``defaultdict`` >>> itemgetter("foo", default=-1)(dd) 0 -The same applies to any user built object that overloads ``__getitem__`` or ``__getattr__`` implementing fallbacks. +The same applies to any user built object that overloads ``__getitem__`` +or ``__getattr__`` implementing fallbacks. Rejected Ideas @@ -176,20 +212,34 @@ Rejected Ideas Multiple Default Values ----------------------- -The idea of allowing multiple default values for multiple attributes or items was considered. +The idea of allowing multiple default values for multiple attributes or +items was considered. -Two alternatives were discussed, using an iterable that must have the same quantity of items than parameters given to ``attrgetter``/``itemgetter``, or using a dictionary with keys matching those names passed to ``attrgetter``/``itemgetter``. +Two alternatives were discussed, using an iterable that must have the +same quantity of items than parameters given to +``attrgetter``/``itemgetter``, or using a dictionary with keys matching +those names passed to ``attrgetter``/``itemgetter``. -The really complex thing to solve in these casse, that would make the feature hard to explain and with confusing corners, is what would happen if an iterable or dictionary is the *unique* default desired for all items. For example:: +The really complex thing to solve in these casse, that would make the +feature hard to explain and with confusing corners, is what would happen +if an iterable or dictionary is the *unique* default desired for all +items. For example:: >>> itemgetter("a", default=(1, 2)({}) (1, 2) >>> itemgetter("a", "b", default=(1, 2))({}) ((1, 2), (1, 2)) -If we allow "multiple default values" using ``default``, the first case in the example above would raise an exception because more items in the default than names, and the second case would return ``(1, 2))``. This is why emerged the possibility of using a different name for multiple defaults (``defaults``, which is expressive but maybe error prone because too similar to ``default``). +If we allow "multiple default values" using ``default``, the first case +in the example above would raise an exception because more items in the +default than names, and the second case would return ``(1, 2))``. This is +why emerged the possibility of using a different name for multiple +defaults (``defaults``, which is expressive but maybe error prone because +too similar to ``default``). -As part of this conversation there was another proposal that would enable multiple defaults, which is allowing combinations of ``attrgetter`` and ``itemgetter``, e.g.:: +As part of this conversation there was another proposal that would enable +multiple defaults, which is allowing combinations of ``attrgetter`` and +``itemgetter``, e.g.:: >>> ig_a = itemgetter("a", default=1) >>> ig_b = itemgetter("b", default=2) @@ -199,15 +249,21 @@ As part of this conversation there was another proposal that would enable multip >>> ig_combined({}) (1, 2) -However, combining ``itemgetter`` or ``attrgetter`` is a totally new behaviour very complex to define, not impossible, but beyond the scope of this PEP. +However, combining ``itemgetter`` or ``attrgetter`` is a totally new +behaviour very complex to define, not impossible, but beyond the scope of +this PEP. -At the end having multiple default values was deemed overly complex and potentially confusing, and a single ``default`` parameter was favored for simplicity and predictability. +At the end having multiple default values was deemed overly complex and +potentially confusing, and a single ``default`` parameter was favored for +simplicity and predictability. Tuple Return Consistency ------------------------ -Another rejected proposal was adding a a flag to always return tuple regardless of how many keys/names/indices were sourced to arguments. E.g.:: +Another rejected proposal was adding a a flag to always return tuple +regardless of how many keys/names/indices were sourced to arguments. +E.g.:: >>> letters = ["a", "b", "c"] >>> itemgetter(1, return_tuple=True)(letters) @@ -215,7 +271,9 @@ Another rejected proposal was adding a a flag to always return tuple regardless >>> itemgetter(1, 2, return_tuple=True)(letters) ('b', 'c') -This would be of a little help for multiple default values consistency, but requires further discussion and for sure is out of the scope of this PEP. +This would be of a little help for multiple default values consistency, +but requires further discussion and for sure is out of the scope of this +PEP. Open Issues @@ -224,21 +282,38 @@ Open Issues Behaviour Equivalence for ``itemgetter`` ---------------------------------------- -We need to define how ``itemgetter`` would behave, if just attempt to access the item and capture exceptions no matter which the object, or validate first if the object provides a ``get`` method and use it to retrieve the item with a default. See examples in the About Posible Implementations subsection before. +We need to define how ``itemgetter`` would behave, if just attempt to +access the item and capture exceptions no matter which the object, or +validate first if the object provides a ``get`` method and use it to +retrieve the item with a default. See examples in the About Posible +Implementations subsection before. -This would help performance for the case of dictionaries, but would make the ``default`` feature somewhat more difficult to explain, and a little confusing if some object that is not a dictionary but provides a ``get`` method is used. Alternatively, we could call ``.get`` *only* if the object is an instance of ``dict``. +This would help performance for the case of dictionaries, but would make +the ``default`` feature somewhat more difficult to explain, and a little +confusing if some object that is not a dictionary but provides a ``get`` +method is used. Alternatively, we could call ``.get`` *only* if the +object is an instance of ``dict``. -In any case, a desirable situation is that we do *not* affect performance at all if the ``default`` is not triggered. Checking for ``.get`` would get the default faster in case of dicts, but implies doing a verification in all cases. Using the try/except model would make it not as fast as it could in the case of dictionaries, but would not introduce delays if the default is not triggered. +In any case, a desirable situation is that we do *not* affect performance +at all if the ``default`` is not triggered. Checking for ``.get`` would +get the default faster in case of dicts, but implies doing a verification +in all cases. Using the try/except model would make it not as fast as it +could in the case of dictionaries, but would not introduce delays if the +default is not triggered. Add a Default to ``getitem`` -------------------------- -It was proposed that we could also enhance ``getitem``, as part of the of this PEP, adding ``default`` also to it. +It was proposed that we could also enhance ``getitem``, as part of the of +this PEP, adding ``default`` also to it. -This will not only improve ``getitem`` itself, but we would also gain internal consistency in the ``operator`` module and in comparison with the ``getattr`` builtin function that also has a default. +This will not only improve ``getitem`` itself, but we would also gain +internal consistency in the ``operator`` module and in comparison with +the ``getattr`` builtin function that also has a default. -The definition could be as simple as the try/except proposed above, so doing ``getitem(obj, name, default)`` would be equivalent to:: +The definition could be as simple as the try/except proposed above, so +doing ``getitem(obj, name, default)`` would be equivalent to:: try: result = obj[name] @@ -251,22 +326,30 @@ The definition could be as simple as the try/except proposed above, so doing ``g How to Teach This ================= -As the basic behaviour is not modified, this new ``default`` can be avoided when teaching ``attrgetter`` and ``itemgetter`` for the first time, and can be introduced only when the functionality need arises. +As the basic behaviour is not modified, this new ``default`` can be +avoided when teaching ``attrgetter`` and ``itemgetter`` for the first +time, and can be introduced only when the functionality need arises. Backwards Compatibility ======================= -The proposed changes are backward-compatible. The ``default`` parameter is optional; existing code without this parameter will function as before. Only code that explicitly uses the new ``default`` parameter will exhibit the new behavior, ensuring no disruption to current implementations. +The proposed changes are backward-compatible. The ``default`` parameter +is optional; existing code without this parameter will function as +before. Only code that explicitly uses the new ``default`` parameter will +exhibit the new behavior, ensuring no disruption to current +implementations. Security Implications ===================== -Introducing a ``default`` parameter does not inherently introduce security vulnerabilities. +Introducing a ``default`` parameter does not inherently introduce +security vulnerabilities. Copyright ========= -This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. From f608cdcd50cb121a20ce2db6061cf2e3771013e5 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Wed, 25 Dec 2024 11:52:16 -0300 Subject: [PATCH 3/7] Fixed date format and a title underlining. --- peps/pep-0790.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0790.rst b/peps/pep-0790.rst index 76491be8fd6..c534abfd114 100644 --- a/peps/pep-0790.rst +++ b/peps/pep-0790.rst @@ -3,7 +3,7 @@ Title: Add a 'default' keyword argument to 'attrgetter' and 'itemgetter' Author: Facundo Batista Status: Draft Type: Standards Track -Created: 22-12-2024 +Created: 22-Dec-2024 Python-Version: 3.14 From 8c62635036c62fab479db9242a1ad5ae71a64203 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Wed, 25 Dec 2024 11:53:32 -0300 Subject: [PATCH 4/7] Fixed date format and a title underlining. --- peps/pep-0790.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0790.rst b/peps/pep-0790.rst index c534abfd114..0fb802bae7b 100644 --- a/peps/pep-0790.rst +++ b/peps/pep-0790.rst @@ -303,7 +303,7 @@ default is not triggered. Add a Default to ``getitem`` --------------------------- +---------------------------- It was proposed that we could also enhance ``getitem``, as part of the of this PEP, adding ``default`` also to it. From 463e88eb639f118447d1ef35791361d609a2b304 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Thu, 26 Dec 2024 16:01:09 -0300 Subject: [PATCH 5/7] Changed PEP number. --- peps/{pep-0790.rst => pep-0769.rst} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename peps/{pep-0790.rst => pep-0769.rst} (99%) diff --git a/peps/pep-0790.rst b/peps/pep-0769.rst similarity index 99% rename from peps/pep-0790.rst rename to peps/pep-0769.rst index 0fb802bae7b..e3e59450acf 100644 --- a/peps/pep-0790.rst +++ b/peps/pep-0769.rst @@ -1,4 +1,4 @@ -PEP: 790 +PEP: 769 Title: Add a 'default' keyword argument to 'attrgetter' and 'itemgetter' Author: Facundo Batista Status: Draft From 508327a6e6f8c91059f2597fca0df1520e884ed7 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Fri, 27 Dec 2024 21:42:29 -0300 Subject: [PATCH 6/7] Changes as per review. --- .github/CODEOWNERS | 2 +- peps/pep-0769.rst | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e5963d3945c..8fa1af1d7ee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -646,11 +646,11 @@ peps/pep-0765.rst @iritkatriel @ncoghlan peps/pep-0766.rst @warsaw peps/pep-0767.rst @carljm peps/pep-0768.rst @pablogsal +peps/pep-0769.rst @facundobatista # ... peps/pep-0777.rst @warsaw # ... peps/pep-0789.rst @njsmith -peps/pep-0790.rst @facundobatista # ... peps/pep-0801.rst @warsaw # ... diff --git a/peps/pep-0769.rst b/peps/pep-0769.rst index e3e59450acf..49e9fb39a16 100644 --- a/peps/pep-0769.rst +++ b/peps/pep-0769.rst @@ -41,7 +41,8 @@ This approach maintains simplicity and avoids the complexity of assigning individual default values to multiple attributes or items. While some discussions considered allowing multiple defaults, the increased complexity and potential for confusion led to favoring a single default -value for all cases (more about this below in Rejected Ideas). +value for all cases (more about this below in `Rejected Ideas +`__). Specification @@ -142,12 +143,14 @@ Using ``default``:: ('bar', 'XYZ') -About Posible Implementations ------------------------------ +.. _PEP 769 About Possible Implementations: + +About Possible Implementations +------------------------------ For the case of ``attrgetter`` is quite direct: it implies using ``getattr`` catching a possible ``AttributeError``. So -``attrgetter("name", default=XYZ)(obj)`` would be like: +``attrgetter("name", default=XYZ)(obj)`` would be like:: try: value = getattr(obj, "name") @@ -170,7 +173,7 @@ understand: attempting ``__getitem__`` and catching a possible exception except (TypeError, IndexError, KeyError): value = XYZ -However, this would be not as eficient as we'd want for particular cases, +However, this would be not as efficient as we'd want for particular cases, e.g. using dictionaries where particularly good performance is desired. A more complex alternative would be:: @@ -183,7 +186,7 @@ more complex alternative would be:: value = XYZ Better performance, more complicated to implement and explain. This is -the first case in the Open Issues section later. +the first case in the `Open Issues `__ section later. Corner Cases @@ -206,6 +209,8 @@ The same applies to any user built object that overloads ``__getitem__`` or ``__getattr__`` implementing fallbacks. +.. _PEP 769 Rejected Ideas: + Rejected Ideas ============== @@ -276,6 +281,8 @@ but requires further discussion and for sure is out of the scope of this PEP. +.. _PEP 769 Open Issues: + Open Issues =========== @@ -285,8 +292,9 @@ Behaviour Equivalence for ``itemgetter`` We need to define how ``itemgetter`` would behave, if just attempt to access the item and capture exceptions no matter which the object, or validate first if the object provides a ``get`` method and use it to -retrieve the item with a default. See examples in the About Posible -Implementations subsection before. +retrieve the item with a default. See examples in the `About Possible +Implementations `__ subsection +above. This would help performance for the case of dictionaries, but would make the ``default`` feature somewhat more difficult to explain, and a little @@ -318,9 +326,9 @@ doing ``getitem(obj, name, default)`` would be equivalent to:: try: result = obj[name] except (TypeError, IndexError, KeyError): - resutl = default + result = default -(however see previous open issue about special case for dictionaries) +(However see previous open issue about special case for dictionaries) How to Teach This From 947b16e53de2b0f02f533fb5a9fbbd4ef3fe067b Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Sun, 5 Jan 2025 08:49:19 -0300 Subject: [PATCH 7/7] Fixed internal links. --- peps/pep-0769.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0769.rst b/peps/pep-0769.rst index 49e9fb39a16..8059175467b 100644 --- a/peps/pep-0769.rst +++ b/peps/pep-0769.rst @@ -42,7 +42,7 @@ individual default values to multiple attributes or items. While some discussions considered allowing multiple defaults, the increased complexity and potential for confusion led to favoring a single default value for all cases (more about this below in `Rejected Ideas -`__). +`__). Specification @@ -186,7 +186,7 @@ more complex alternative would be:: value = XYZ Better performance, more complicated to implement and explain. This is -the first case in the `Open Issues `__ section later. +the first case in the `Open Issues `__ section later. Corner Cases @@ -293,7 +293,7 @@ We need to define how ``itemgetter`` would behave, if just attempt to access the item and capture exceptions no matter which the object, or validate first if the object provides a ``get`` method and use it to retrieve the item with a default. See examples in the `About Possible -Implementations `__ subsection +Implementations `__ subsection above. This would help performance for the case of dictionaries, but would make