So it only works if ALL possible bar values are limited to those that evaluate to True on truth test. And even if I have such cases, when this is the case, I restrain from doing this for the sake of consistency of coding style and to eliminate risk if I ever decide to introduce new input options that do not comply with this restriction. Just to be on the safe side.
But not having lengthy, hard to read expressions to do such a simple thing is the key here isn’t it?
So 2 lines instead of 1, but easy to read, risk-free simple code.
def __init__(self, bar, default=1):
if bar is None:
self.bar = default
else:
self.bar = bar
Or the 1-line if-else, but (to repeat myself) these 2 don’t read nicely and lack aesthetics for such a simple case as this.
IMO, what could probably be a good (time tested and convenient) solution is to introduce something similar to C-style, low-level `ifelse` expression.
string result = (time < 18) ? "Good day." : "Good evening.”;
My personal opinion is that 1-line pythonic if-else expression lacks pythonic aesthetics.
The PEP505 looks nice, but it is mostly concerned with `None`, which I IMO is the reason it didn’t go through. It’s the same as with sentinels:
https://peps.python.org/pep-0661/
The proposal is too narrow and specific, to be implemented in core python. In other words, it only solves one corner case, but not the root issue. My solution to PEP661 was to use `unittest.sentinel` so I can define my own `None` alternatives.
Now, if PEP505 was accepted, then it would only work for `None`, but not for user-defined sentinels, meaning it is not a good solution either - something more general is needed. What I think could potentially be optimal is either:
1. result = bar is None ? default : bar
However, in this case, new operator had to be introduced instead of “:”, which I am sure would arise many discussions. Although, I think this is a very elegant expression
2. result = ifelse(bar is None, default, bar)
Having such well-optimised builtin in core python, would also be a good solution without introduction of any new operators.
Now, this doesn’t directly solve your initial proposal:
ifelse(dict.get('key') is None, {}, dict.get('key')).get('child_key')
However, then it could be easier to define a new method when subclassing `dict` or `UserDict`
from unittest.sentinel import SentinelNotGiven = Sentinel.NotGiven
class PlayDict(dict):
def get_or(self, key, default=NotGiven, arbitrary=None):
value = self.get(key, default)
return value is NotGiven ? arbitrary : value
# OR
return ifelse(value is NotGiven, arbitrary, value)
Whether this is included in core-python or not is another matter. It is not as elegant as proposal in PEP505, but it is not limited to `None` sentinel, while making use of nicely-read 1-liner.
So IMO, a more elegant if-else 1-liner is potentially a happy middle, which would cover many different cases, although not as elegantly as PEP505.
Dom Grigonis wrote:
I like this. Something that I would use for sure.
I have used a lot of:
```
(value: None | object ) or default
```
, but I stopped for obvious reasons. However, I miss those days, when I was ignorant that this is not a good idea.
On 11 Jul 2023, at 01:17, David Mertz, Ph.D. david.mertz@gmail.com wrote:
This is basically PEP 505 – None-aware operators (https://peps.python.org/pep-0505/ https://peps.python.org/pep-0505/).
I've generally been opposed to that, but clearly some serious and smart Pythonistas have supported it. That PEP is a bigger lift than your suggestion, but correspondingly more general.
On Mon, Jul 10, 2023, 6:04 PM Jothir Adithyan <adithyanjothir@gmail.com mailto:adithyanjothir@gmail.com> wrote:
Hi everyone,
I would like to briefly present my idea regarding the `get` function commonly used with dictionaries. When working with large amounts of JSON data, I often encounter code that doesn't feel very Pythonic to me.
Problem Statement:
The `get` function allows chaining of method calls, which is quite useful when working with dictionaries. It also has a convenient `default` parameter that returns a default value if the key is not found in the dictionary. This feature makes it safe and easy to use. However, problems arise when the dictionary contains the key we are accessing, but the corresponding value is `None`. In such cases, subsequent `get` calls fail because the `get` method belongs to objects of type `dict` and not `None`. To address this, I propose adding a new parameter to the `get` function or introducing a new function called `get_or` that swiftly handles this issue. This new parameter, called `arbitrary`, would accept an arbitrary value to be returned by subsequent `get` calls in case the retrieved value of the key is `None`.
Assumptions:
The problem statement is based on a few assumptions:
You prefer chaining `get` statements for cleaner code.
You expect at least some of the `get` methods to return `None`.
You want to avoid the hassle of using `try` and `except` for every `get` chain.
If you fall into the category of people who wish for a simpler way to work with dictionaries and handle large amounts of data, I hope you can empathise with this proposal.
I have made a simple implementation by modifying the `get` method, which is below this thread. I would appreciate your valuable input on this feature. Before diving into coding, I want to make sure this is not a bad idea waiting to reveal itself in the dark.
Thank you for taking the time to read this thread. Your feedback is greatly appreciated.
Best regards,
Jothir Adithyan
**Runnable Version**
https://replit.com/@Adithyan71/GetOr https://replit.com/@Adithyan71/GetOr
**Code Samples.**
class PlayDict(dict):
def get_or(self, key, arbitrary=None, default=None):
if not self.__contains__(
key
): # case 1 the key does not exist hence the default value
return default
elif (
self[key] is None
): # case 2 key does exist but the value is None return the arb value
return arbitrary
return self[key] # case 3 the key is present and the value is not None
import contextlib
parent_dict = PlayDict()
parent_dict['k1'] = None
parent_dict['k2'] = {"child_key": "val"} # Parent dict can contain nested dicts
with contextlib.suppress(AttributeError):
result = parent_dict.get("k1", {}).get("child_key") # This will raise AttributeError
result = parent_dict.get_or("k1", default={}, arbitrary={}).get("child_key") # This will work
print(result)
Python-ideas mailing list -- python-ideas@python.org mailto:python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org mailto:python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/ https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IELDCU... https://mail.python.org/archives/list/python-ideas@python.org/message/IELDCURRVQXCPGD4EPPLL7MYWJT4XHKV/
Code of Conduct: http://python.org/psf/codeofconduct/ http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4BCGPZ...
Code of Conduct: http://python.org/psf/codeofconduct/
Hey Dom, thank you for your time.
Could you please provide a couple of reasons why this idea might not be favorable? I would like to have a clear understanding and ensure my assumptions are correct.
Regards,
Jothir Adithyan
_______________________________________________
Python-ideas mailing list --
python-ideas@python.orgTo unsubscribe send an email to
python-ideas-leave@python.orghttps://mail.python.org/mailman3/lists/python-ideas.python.org/Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4DZXMGL4UZ7HBGJB5MC2SCMLVSGVTQCW/
Code of Conduct: http://python.org/psf/codeofconduct/