def fu(bar, default=1): bar = bar or default return bar print(fu(None)) # 1 - as expected print(fu(2)) # 2 - as expected print(fu(0)) # 1 - not as expected class A: def __bool__(self): return False print(fu(A()) # 1 - not as expected 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. Obviously, one could do: def fu(bar, default=1): bar = bar if bar is not None else default return bar But not having lengthy, hard to read expressions to do such a simple thing is the key here isn’t it? So my latest resolve for such cases is simply: def fu(bar, default=1): if bar is None: bar = default return bar So 2 lines instead of 1, but easy to read, risk-free simple code. In case of class construction, I suffer 1 extra assignment here: def __init__(self, bar, default=1): self.bar = bar if bar is None: self.bar = default Which I don’t like! Extra assignment is not ideal. Of course I could use: 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/ <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 Sentinel NotGiven = 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.
On 15 Jul 2023, at 21:07, Jothir Adithyan <adithyanjothir@gmail.com> wrote:
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/IELDCU... 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.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/4DZXMG... Code of Conduct: http://python.org/psf/codeofconduct/