Just to add. I haven’t thought about evaluation. Thus, to prevent evaluation of unnecessary code, introduction of C-style expression is needed anyways: > 1. result = bar is None ? default : bar
The below would have to evaluate all arguments, thus not achieving benefits of PEP505. > 2. result = ifelse(bar is None, default, bar) So I would vote for something similar to: > result = bar is None ? default : bar Where default and bar is only evaluated if needed. Although not to the extent as initially intended, it would offer satisfiable solutions to several proposals. > On 15 Jul 2023, at 22:37, Dom Grigonis <dom.grigo...@gmail.com> wrote: > > 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 <adithyanjot...@gmail.com >> <mailto:adithyanjot...@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.me...@gmail.com >>>> <mailto:david.me...@gmail.com> wrote: >>>> This is basically PEP 505 – None-aware operators >>>> (https://peps.python.org/pep-0505/ <https://peps.python.org/pep-0505/> >>>> 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 <adithyanjot...@gmail.com >>>> <mailto:adithyanjot...@gmail.com> mailto:adithyanjot...@gmail.com >>>> <mailto:adithyanjot...@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> >>>> 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> mailto:python-ideas@python.org >>>> <mailto:python-ideas@python.org> >>>> To unsubscribe send an email to python-ideas-le...@python.org >>>> <mailto:python-ideas-le...@python.org> >>>> mailto:python-ideas-le...@python.org <mailto:python-ideas-le...@python.org> >>>> https://mail.python.org/mailman3/lists/python-ideas.python.org/ >>>> <https://mail.python.org/mailman3/lists/python-ideas.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>... >>>> >>>> https://mail.python.org/archives/list/python-ideas@python.org/message/IELDCURRVQXCPGD4EPPLL7MYWJT4XHKV/ >>>> >>>> <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/> >>>> http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/> >>>> _______________________________________________ >>>> Python-ideas mailing list -- python-ideas@python.org >>>> <mailto:python-ideas@python.org> >>>> To unsubscribe send an email to python-ideas-le...@python.org >>>> <mailto:python-ideas-le...@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/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 >> <mailto:python-ideas@python.org> >> To unsubscribe send an email to python-ideas-le...@python.org >> <mailto:python-ideas-le...@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/4DZXMGL4UZ7HBGJB5MC2SCMLVSGVTQCW/ >> Code of Conduct: http://python.org/psf/codeofconduct/ >
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/4X5XXPUB5B5PKAR4HUWMWF45ZNYQ4GKX/ Code of Conduct: http://python.org/psf/codeofconduct/