Proposal for get_or function in Python dictionaries
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 **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) ```
This is basically PEP 505 – None-aware operators ( 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> 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
**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 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/IELDCU... Code of Conduct: http://python.org/psf/codeofconduct/
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/
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
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/
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.grigonis@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 <adithyanjothir@gmail.com <mailto: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 <mailto: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/> 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> mailto: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> 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-leave@python.org <mailto:python-ideas-leave@python.org> mailto: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/> 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/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/> 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-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/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-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/4DZXMG... Code of Conduct: http://python.org/psf/codeofconduct/
I am well aware of that. I just argue that it is not very pleasant to read. Personally, I am not a big user of it. C’s although maybe doesn’t have words to read, but is much more pleasant in both conciseness and logical sequence. Maybe it is not a big issue for others, but if C’s style conditional expression existed, it would significantly change the way I write things. So just checking. :)
On 16 Jul 2023, at 00:06, Paul Moore <p.f.moore@gmail.com> wrote:
On Sat, 15 Jul 2023 at 21:09, Dom Grigonis <dom.grigonis@gmail.com <mailto:dom.grigonis@gmail.com>> wrote: So I would vote for something similar to:
result = bar is None ? default : bar
result = default if bar is None else bar
Python has a conditional expression already. Paul
On 15/07/2023 21:08, Dom Grigonis wrote:
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.
Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent result = default if bar is None else bar or if you prefer result = bar if bar is not None else default Perhaps you didn't know about this construction? It does exactly the same as the C version and is more readable. Or am I missing something? Best wishes Rob Cliffe
It does exactly the same as the C version and is more readable. Or am I missing something?
My point is exactly that it is not easily readable compared to C version. Also, unnecessarily verbose. The order of components is rather awkward. I came to this, because people seem to want one-liners for certain things and what I came up with is that maybe more concise if-else expression could help. # Fairly reasonable case. def foo(a: bool, c: float, d: float) val = a ? (c ? c : d) : (d ? d : c) return val # Comapred to def bar(a: bool, c: float, d: float) val = (c if c else d) if a else (d if d else c) return val Maybe for someone who majored in languages python’s if-else is more easily understood. To me, personally, 2nd one is unbearable, while 1st one is fairly pleasant and satisfying. This whole thing started from dict’s `get` extension: def get_substitute(self, key, default=None, subs=()): return key in self ? (self[key] := val in subs ? subs[val] : val) : default I dare you to do a 1-liner with current if-else. Also, https://peps.python.org/pep-0505/ <https://peps.python.org/pep-0505/> Why was it even considered if it doesn’t introduce any new functionality? I also dare you go through that PEP’s examples and re-write them with current if-else expression. So maybe, just maybe, making already existing expression more readable can also be a valid suggestion? As I said, in my opinion it would solve many existing queries and requests, just because certain things would become very pleasant and obvious how to do in simple one-liners. Simpler and more logically convenient if-else combined with other elegant python expressions would potentially fill that gap. From where I currently stand it just seems fairly happy middle solution between: very concise narrow functionality requests and very verbose ways of how they need to be done at the moment. I fully appreciate how likely what I am proposing is going to be even considered. But I really think it should be.
On 17 Jul 2023, at 18:09, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
On 15/07/2023 21:08, Dom Grigonis wrote:
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.
Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Perhaps you didn't know about this construction?
Best wishes Rob Cliffe
On 2023-07-17 18:10, Dom Grigonis wrote:
It does exactly the same as the C version and is more readable. Or am I missing something?
My point is exactly that it is not easily readable compared to C version. Also, unnecessarily verbose. The order of components is rather awkward.
I came to this, because people seem to want one-liners for certain things and what I came up with is that maybe more concise if-else expression could help.
# Fairly reasonable case.
def foo(a:bool, c:float, d:float) val= a? (c? c : d) : (d? d : c) return val
# Comapred to def bar(a:bool, c:float, d:float) val= (cif celse d)if aelse (dif delse c) return val
Maybe for someone who majored in languages python’s if-else is more easily understood. To me, personally, 2nd one is unbearable, while 1st one is fairly pleasant and satisfying.
This whole thing started from dict’s `get` extension:
def get_substitute(self, key, default=None, subs=()): return keyin self ? (self[key] := valin subs? subs[val] : val) : default
I dare you to do a 1-liner with current if-else.
def get_substitute(self, key, default=None, subs=()): return (self[key] := subs[val] if val in subs else val) if key in self else default Where does 'val' come from? [snip]
On Tue, 18 Jul 2023 at 03:13, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Maybe for someone who majored in languages python’s if-else is more easily understood. To me, personally, 2nd one is unbearable, while 1st one is fairly pleasant and satisfying.
This is a REALLY good example of how hard it is to be objective about syntax. Being familiar with something really truly does make it immensely better - for you. You're comfortable with the C syntax. That's great! So am I. But that isn't a good indication of how it would be accepted by someone who isn't familiar with either syntax. The ?: syntax has the advantage that the evaluation order is left-to-right, which is the most common (though far from universal) evaluation order. That is a definite advantage, to be sure, but perhaps not as big as you might think. The if/else syntax is more consistent with other Python syntax by using words, though. Ultimately, *all* syntax has to be learned. ChrisA
This is NOT a good example, my first language was R, second - python, third matlab, etc. I only became familiar with C/C++ after several years of coding experience. This is why, I would dare to say that this preference of mine is not affected by prejudices.
On 17 Jul 2023, at 20:44, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, 18 Jul 2023 at 03:13, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Maybe for someone who majored in languages python’s if-else is more easily understood. To me, personally, 2nd one is unbearable, while 1st one is fairly pleasant and satisfying.
This is a REALLY good example of how hard it is to be objective about syntax. Being familiar with something really truly does make it immensely better - for you. You're comfortable with the C syntax. That's great! So am I. But that isn't a good indication of how it would be accepted by someone who isn't familiar with either syntax.
The ?: syntax has the advantage that the evaluation order is left-to-right, which is the most common (though far from universal) evaluation order. That is a definite advantage, to be sure, but perhaps not as big as you might think. The if/else syntax is more consistent with other Python syntax by using words, though.
Ultimately, *all* syntax has to be learned.
ChrisA _______________________________________________ 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/NGJ5GH... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 18 Jul 2023 at 04:37, Dom Grigonis <dom.grigonis@gmail.com> wrote:
This is NOT a good example, my first language was R, second - python, third matlab, etc.
I only became familiar with C/C++ after several years of coding experience.
This is why, I would dare to say that this preference of mine is not affected by prejudices.
Never said C was your first language, only that you're familiar with it. And that familiarity inevitably WILL affect your perception, as per what I said. ChrisA
Never said C was your first language, only that you're familiar with it.
But that is more like an obvious fact about everything rather than a counter-argument. Your e-mail, at least how I interpreted it, was a fairly straight forward accent on my strong bias with a flavour of making it an example for everyone to note. It would supposedly hold as an argument if I was used to C more than python, which would indicate my strong bias. While now, I am mostly interested in python and it’s long term trajectory, given I suspect I will be using it in 2 or 3 years time. If I see a pseudocode of a non-existent language and I get an idea how to make python nicer, I will use it. But the argument that I am familiar with something that others are not is not really a counter-argument (to anything). Except maybe serves to emphasise a general human aversion to change, which is naturally less for someone who is a bit familiar. So yes, I am less averse to such change because of 2 factors: 1. I am familiar with expression that I am proposing 2. This is my proposal But neither 1 nor 2 are valid counter-arguments against what I am proposing. And yes,
On 17 Jul 2023, at 23:57, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, 18 Jul 2023 at 04:37, Dom Grigonis <dom.grigonis@gmail.com> wrote:
This is NOT a good example, my first language was R, second - python, third matlab, etc.
I only became familiar with C/C++ after several years of coding experience.
This is why, I would dare to say that this preference of mine is not affected by prejudices.
Never said C was your first language, only that you're familiar with it. And that familiarity inevitably WILL affect your perception, as per what I said.
ChrisA _______________________________________________ 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/ZC2O5R... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 18 Jul 2023 at 04:37, Dom Grigonis <dom.grigonis@gmail.com> wrote:
This is why, I would dare to say that this preference of mine is not affected by prejudices.
Of course it's affected by prejudices -- all our preferences are. A sample of one "I find it more readable" is about as useful as any sample of one to represent a population. Personally, I find the C syntax completely opaque and the Python syntax totally understandable the first time I saw it -- and it's very much more "Pythonic". But that's influenced by my prejudice. But anyway, this conversation is 20 (!) years too late[*] -- and it did take place then. It's not going to change now. -CHB [*] https://peps.python.org/pep-0308/ -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
What I meant is that it is not affected by prejudices to such a degree that a 'challenger' wanted to make it look. “I find it more readable” is a fair statement. The recognition of subjectivity is very transparent in it. In other words, such statement is not trying to be bigger than it actually is. I realise that. I actually sometimes do use conditional `ifelse` and well aware of it’s existence. I am just trying to connect dots here. Some proposals here are answered very clearly and in line with: "There should be one-- and preferably only one --obvious way to do it.” And some fall into category “there are workarounds, just deal with it". As I said, it seems that (at least to me) there is a big part of them that are pointing towards this direction - those that are confused about the elegant and simple way of doing certain simple things. Maybe this 20-year late discussion will have some impact on future python expressions. E.g. some new expression will be a superset of `ifelse` and this will be taken into account. Maybe not, but hopefully it had at least a bit of positive value.
On 18 Jul 2023, at 01:49, Christopher Barker <pythonchb@gmail.com> wrote:
On Tue, 18 Jul 2023 at 04:37, Dom Grigonis <dom.grigonis@gmail.com <mailto:dom.grigonis@gmail.com>> wrote:
This is why, I would dare to say that this preference of mine is not affected by prejudices.
Of course it's affected by prejudices -- all our preferences are. A sample of one "I find it more readable" is about as useful as any sample of one to represent a population.
Personally, I find the C syntax completely opaque and the Python syntax totally understandable the first time I saw it -- and it's very much more "Pythonic".
But that's influenced by my prejudice.
But anyway, this conversation is 20 (!) years too late[*] -- and it did take place then. It's not going to change now.
-CHB
[*] https://peps.python.org/pep-0308/ <https://peps.python.org/pep-0308/>
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ 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/YQGA4K... Code of Conduct: http://python.org/psf/codeofconduct/
Just to add, I think your statement actually works in my favour rather than against my proposal. People from this group are potentially biased and lack objectivity against what I am proposing because they are familiar with python and more experienced ones participated in “carved in stone” decision regarding already existent `ifelse` expression. So I expected that my proposal might even look humorous to some. E.g. In survey I got a comment “to learn python if I want to use it”. I am doing my best, just some parts of it are hard to digest in the context of other parts that are so easily digestible.
On 17 Jul 2023, at 21:37, Dom Grigonis <dom.grigonis@gmail.com> wrote:
This is NOT a good example, my first language was R, second - python, third matlab, etc.
I only became familiar with C/C++ after several years of coding experience.
This is why, I would dare to say that this preference of mine is not affected by prejudices.
On 17 Jul 2023, at 20:44, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, 18 Jul 2023 at 03:13, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Maybe for someone who majored in languages python’s if-else is more easily understood. To me, personally, 2nd one is unbearable, while 1st one is fairly pleasant and satisfying.
This is a REALLY good example of how hard it is to be objective about syntax. Being familiar with something really truly does make it immensely better - for you. You're comfortable with the C syntax. That's great! So am I. But that isn't a good indication of how it would be accepted by someone who isn't familiar with either syntax.
The ?: syntax has the advantage that the evaluation order is left-to-right, which is the most common (though far from universal) evaluation order. That is a definite advantage, to be sure, but perhaps not as big as you might think. The if/else syntax is more consistent with other Python syntax by using words, though.
Ultimately, *all* syntax has to be learned.
ChrisA _______________________________________________ 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/NGJ5GH... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 18 Jul 2023 at 08:56, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Just to add, I think your statement actually works in my favour rather than against my proposal.
People from this group are potentially biased and lack objectivity against what I am proposing because they are familiar with python and more experienced ones participated in “carved in stone” decision regarding already existent `ifelse` expression.
Of course we're biased. Everyone is biased. If you don't think you're biased, you just haven't seen your biases yet. A few of mine: * I have functional eyesight * I have a US-English layout "qwerty" keyboard * My internet connection, while decent, is not nearly as fast as some * I spend many hours a day writing code (as opposed to a novice, student, or very casual programmer, who all will have other priorities) * I use Linux exclusively and have no experience with Windows 11 * And yes, I know C. Also Ruby, Pike, JavaScript, and a ton of others. Each of these affects my opinions of what's good and what's not. And those are just the ones I could think of, off the top of my head. Some of them I actively fight against; others I simply recognize (for example, when people are discussing a change that would only affect Windows users, I'll let other people judge, since I can't adequately assess its impact).
So I expected that my proposal might even look humorous to some. E.g. In survey I got a comment “to learn python if I want to use it”. I am doing my best, just some parts of it are hard to digest in the context of other parts that are so easily digestible.
Humorous? Or just completely pointless? I don't think anyone here has said that your proposal is a joke.
Maybe this 20-year late discussion will have some impact on future python expressions. E.g. some new expression will be a superset of `ifelse` and this will be taken into account. Maybe not, but hopefully it had at least a bit of positive value.
https://peps.python.org/pep-0622/ Yet again, an improvement in expressiveness, though not in compactness. ChrisA
On 7/17/23 1:10 PM, Dom Grigonis wrote:
This whole thing started from dict’s `get` extension: def get_substitute(self, key, default=None, subs=()): return keyin self ? (self[key] := valin subs? subs[val] : val) : default I dare you to do a 1-liner with current if-else.
Why are we trying to write one-liners in the first place? Conditional expressions are a rare spice that should be used sparingly. No matter the syntax, three of them nested together isn't readable code, it's a puzzle. --Ned.
On 17 Jul 2023, at 18:09, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
On 15/07/2023 21:08, Dom Grigonis wrote:
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.
Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Perhaps you didn't know about this construction?
Best wishes Rob Cliffe
_______________________________________________ Python-ideas mailing list --python-ideas@python.org To unsubscribe send an email topython-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived athttps://mail.python.org/archives/list/python-ideas@python.org/message/Q4HZ5M... Code of Conduct:http://python.org/psf/codeofconduct/
Why are we trying to write one-liners in the first place? Conditional expressions are a rare spice that should be used sparingly. No matter the syntax, three of them nested together isn't readable code, it's a puzzle.
Why are they a rare spice that should be used sparingly? Is it in line with python best practices? Anything to support this? I think python list comprehensions is a direct antithesis to what you are saying. I thought they are considered to be a good and preferable practice, even nested cases. We even got a new operator “:=“ to help us with those beautiful one-liners (or not) to move towards aesthetics and brevity. Also, I disagree that the referred code isn’t readable. My point is exactly that such code is not readable and is a puzzle if current conditional expression is used. While having a more elegant if-else expression, this would not be a case. Combination of list comprehensions, “:=“ operator and good conditional 1-liners could be an excellent toolkit to do complex things in simple, readable and brief manner. Now I think all is there, except if-else expression does not satisfy in either of those dimensions, namely brevity & readability (maybe it is better readable than C's for someone with major in languages…?). So I am just trying to gauge if anyone else feels this way. As I already said, my code would look fairly differently if anything similar to what I am proposing existed. And I do not have a bias towards C or C++, 99% of what I do is python so if anything I am biased towards how python does things, not the other way round. Is there any place
On 17 Jul 2023, at 23:09, Ned Batchelder <ned@nedbatchelder.com> wrote:
On 7/17/23 1:10 PM, Dom Grigonis wrote:
This whole thing started from dict’s `get` extension: def get_substitute(self, key, default=None, subs=()): return key in self ? (self[key] := val in subs ? subs[val] : val) : default I dare you to do a 1-liner with current if-else.
Why are we trying to write one-liners in the first place? Conditional expressions are a rare spice that should be used sparingly. No matter the syntax, three of them nested together isn't readable code, it's a puzzle.
--Ned.
On 17 Jul 2023, at 18:09, Rob Cliffe <rob.cliffe@btinternet.com <mailto:rob.cliffe@btinternet.com>> wrote:
On 15/07/2023 21:08, Dom Grigonis wrote:
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.
Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Perhaps you didn't know about this construction?
Best wishes Rob Cliffe
_______________________________________________ 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/Q4HZ5M... <https://mail.python.org/archives/list/python-ideas@python.org/message/Q4HZ5ME6V473L25AV33BA6C7JMXTI2PJ/> 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/IBDNP5... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 18 Jul 2023 at 06:40, Dom Grigonis <dom.grigonis@gmail.com> wrote:
We even got a new operator “:=“ to help us with those beautiful one-liners (or not) to move towards aesthetics and brevity.
Was that really the intention? Because I was there when PEP 572 was written, and I don't recall "beautiful one-liners" as one of the justifying reasons. Use around if/while conditions was a strong reason, and yes, that can save a line, but "beautiful one-liners" hasn't generally been a justifying factor in any Python feature. ChrisA
Was that really the intention? Because I was there when PEP 572 was written, and I don't recall "beautiful one-liners" as one of the justifying reasons. Use around if/while conditions was a strong reason, and yes, that can save a line, but "beautiful one-liners" hasn't generally been a justifying factor in any Python feature.
Never said about the intention, just stated that I see it as being a part of their construction. However, in the PEP you have referred and also as I remember reading python’s “what’s new”, list comprehensions and other 1-liners were close to half of all examples.
On 18 Jul 2023, at 00:04, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, 18 Jul 2023 at 06:40, Dom Grigonis <dom.grigonis@gmail.com> wrote:
We even got a new operator “:=“ to help us with those beautiful one-liners (or not) to move towards aesthetics and brevity.
Was that really the intention? Because I was there when PEP 572 was written, and I don't recall "beautiful one-liners" as one of the justifying reasons. Use around if/while conditions was a strong reason, and yes, that can save a line, but "beautiful one-liners" hasn't generally been a justifying factor in any Python feature.
ChrisA _______________________________________________ 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/MJI5UN... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 18 Jul 2023 at 08:38, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Was that really the intention? Because I was there when PEP 572 was written, and I don't recall "beautiful one-liners" as one of the justifying reasons. Use around if/while conditions was a strong reason, and yes, that can save a line, but "beautiful one-liners" hasn't generally been a justifying factor in any Python feature.
Never said about the intention, just stated that I see it as being a part of their construction.
However, in the PEP you have referred and also as I remember reading python’s “what’s new”, list comprehensions and other 1-liners were close to half of all examples.
That is very true. Compactness makes for great "what's new" entries. They want to be easily skimmable, which means they need to be short. That's a constraint that isn't true of real-world code, and it WILL cause the what's-new entries to be a bit nonrepresentative. ChrisA
Dom Grigonis writes:
I came to this, because people seem to want one-liners for certain things and what I came up with is that maybe more concise if-else expression could help.
But "encourages one-liners" is generally considered an anti-pattern in Python. Let's see why,
# Fairly reasonable case.
def foo(a: bool, c: float, d: float) val = a ? (c ? c : d) : (d ? d : c) return val
What's not so great here? 1. The argument names are not going to be single characters, unless you intentionally name that way for the sake of one-line-ism. That severely detracts from your claim of readability. The one-liner is arguably[1] more readable than the Python version, but you've definitely made the whole function less readable. 2. Comparing floats, even against zero, is a bad idea. So I would wrap them in math.isclose: val = a ? (not isclose(c, 0) ? c : d) : (not isclose(d, 0) ? d : c) Pretty long, not that easy to read. Of course if you had intelligible variable names it would be worse. The point is *not* to give you a hard time about comparing floats for equality, we've all done that. It's that in even in this example, done safely you have more complex expressions than just variable references. In general you will have function calls adding parentheses -- and thus confusion. Perhaps there will be lists or tuples involved. You can precompute them and assign them to short name variables, but then you lose the one-liner-ness. 3. OK, how about ints then? Yes, that would work, but how frequently are you going to be comparing against 0? Here's a more realistic case, with readable short argument names: def clamp_int(n: int, lo: int, hi: int): # I wouldn't elide this test because if lo > hi you get a # plausible but necessarily erroneous value if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') val = n < lo ? lo : (n > hi ? hi : n) return val Agreed, the expression using Python's syntax would be worse, but I think this is much more readable: def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') if n < lo: val = lo elif n > hi: val = hi else: val = n return val and it would be more readable yet if you got rid of the assignments and just returned the value as soon as you see it: def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') # Hi, David! Default first!! if lo <= n <= hi: # Yes, Virginia, valid Python! return n elif n > hi: return hi else: return lo (Yes, I know that some folks argue that suites should have one entry point and one exit point, but I don't think it's a problem here because of the extreme symmetry and simplicity of the conditional. IMHO YMMV of course)
I dare you to do a 1-liner with current if-else.
I do use ternary expressions occasionally, but almost never nested. Writing one-liners is never a goal for me, in part because my old eyes can't take in more than 35-40 characters in a gulp.
So maybe, just maybe, making already existing expression more readable can also be a valid suggestion?
We *won't do that*, because of backward compatibility. The syntax of an exist expression cannot change without invalidating a lot of existing code. That's one reason why the bar to new syntax is so high, and it took so long to get the ternary expression: you really want to get it right. That's why Python (like C!) prefers to add to the stdlib rather than the language. Now, you can add a new one that does the same thing, and that's been done. IIRC it took a while to get +=, and C-like ++/-- increment operators have been requested over and over again. AFAIK the async syntaxes do nothing that can't be done with generators, but they make it a lot easier to do it right in the most common cases.
As I said, in my opinion it would solve many existing queries and requests, just because certain things would become very pleasant and obvious how to do in simple one-liners.
One-liners are almost by definition less readable. Even in math papers we break equations into a series of definitions and then build up the final equation from there. As M. Spivak put it in *Calculus on Manifolds*, Stokes' theorem shares three important attributes with many fully evolved major theorems: 1. It is trivial. 2. It is trivial because the terms appearing in it have been properly defined. 3. It has significant consequences. When I have the time and knowledge, I aspire to programming that way. :-) YMMV
Simpler and more logically convenient if-else combined with other elegant python expressions would potentially fill that gap.
I guess. It's certainly true that Python pre-walrus operator had no good solution to the "loop and a half" problem. But I've been programming long enough it's hard to get myself to use the walrus operator in the "assign and return" fashion that C's assignment operator so frequently is.
From where I currently stand it just seems fairly happy middle solution between: very concise narrow functionality requests and very verbose ways of how they need to be done at the moment.
But the functionality you request is extremely narrow in a different sense. At least the way people who develop Python (and GNU Mailman, where I program Python in anger, as they say) taught me, very frequently the most concise expression is not the most readable, and "readability counts". So as above I would likely exand these expressions into statement syntax in many cases anyway, despite having a more concise syntax.
I fully appreciate how likely what I am proposing is going to be even considered. But I really think it should be.
I think you will find almost noone in Python development who would agree that it *should* be. Even if you 100% discount the "20 years late" objection that stops it before it gest started. A lot of the arguments you make would be good arguments, or would have the form of good arguments, for other features. Your "sense" of language development is good, IMO. But if you want to contribute to Python development you need to choose the features to support more in line with conventional wisdom. For example, Chris A's PEP on default argument processing (is that the PEP 671 mentioned earlier?) would resolve what many consider to be a wart, and it hasn't been rejected, just postponed. Or you could choose one that is not merely duplicating existing functionality with syntax ore to your taste. Steve
On Tue, 18 Jul 2023 at 23:41, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
2. Comparing floats, even against zero, is a bad idea. So I would wrap them in math.isclose:
val = a ? (not isclose(c, 0) ? c : d) : (not isclose(d, 0) ? d : c)
That's overly simplistic; there are a lot of times when it's absolutely fine to compare floats for equality, particularly with zero, where the idea of "close" can be ill-defined. But this is the difficulty with toy examples - we have no idea how realistic this actually is. For now, though, I'd be inclined to keep the semantics unchanged and just look at the syntax.
In general you will have function calls adding parentheses -- and thus confusion. Perhaps there will be lists or tuples involved. You can precompute them and assign them to short name variables, but then you lose the one-liner-ness.
This is true, almost trivially so; one-liners aren't all that common because real-world use-cases are often complicated. However, they DO exist. It'd be nice to have really good examples but we may well not have that luxury.
3. OK, how about ints then? Yes, that would work, but how frequently are you going to be comparing against 0? Here's a more realistic case, with readable short argument names:
def clamp_int(n: int, lo: int, hi: int): # I wouldn't elide this test because if lo > hi you get a # plausible but necessarily erroneous value if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') val = n < lo ? lo : (n > hi ? hi : n) return val
Agreed, the expression using Python's syntax would be worse, but I think this is much more readable:
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') if n < lo: val = lo elif n > hi: val = hi else: val = n return val
and it would be more readable yet if you got rid of the assignments and just returned the value as soon as you see it:
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') # Hi, David! Default first!! if lo <= n <= hi: # Yes, Virginia, valid Python! return n elif n > hi: return hi else: return lo
(Yes, I know that some folks argue that suites should have one entry point and one exit point, but I don't think it's a problem here because of the extreme symmetry and simplicity of the conditional. IMHO YMMV of course)
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'{lo = } > {hi = }') return max(lo, min(n, hi)) Nobody said we weren't allowed to use builtins, right? :)
Now, you can add a new one that does the same thing, and that's been done. IIRC it took a while to get +=
Note that augmented operators are not simply shorthands for the expanded version. There are a few differences between: a.b.c.d.e += value and a.b.c.d.e = a.b.c.d.e + value including that the basis object (a.b.c.d) would only be evaluated once, and especially, the addition is done in-place if supported (eg with lists).
and C-like ++/-- increment operators have been requested over and over again. AFAIK the async syntaxes do nothing that can't be done with generators, but they make it a lot easier to do it right in the most common cases.
A plain async function pretty much IS a generator, but without the risk of removing the last await point and having the function stop being a generator. But async generators are also a thing, and there's no easy way to make a generator-generator with two different types of yield in it. But yes, in general, these aren't drastically new pieces of functionality - they're nice improvements to readability and expressiveness. I can write an application that uses generators instead of async functions (and my current flagship non-Python project does exactly that), as long as I'm not using generators for anything else. I can write something with no augmented assignment operators, as long as I always use list extend instead of addition, and don't mind the reevaluation of intermediate components (which, most of the time, is only a performance matter, not a correctness one). But it's definitely nice to have them. ChrisA
A plain async function pretty much IS a generator, but without the risk of removing the last await point and having the function stop being a generator. I am still lost in async space. From my initial benchmarking it seems that the complexity that async is introducing hardly justifies itself in performance. In other words, if it is done using `async` it’s performance is very similar to `gevent`, meaning, that all that hassle doesn’t justify itself in performance space, which is one of the major selling points.
If async is done with `yield`, then it could potentially be justified in performance-critical applications. I am not 100% certain about this as I haven't compared everything thoroughly, but these were the findings from my initial fairly crude testing. Am I missing something here?
But async generators are also a thing, and there's no easy way to make a generator-generator with two different types of yield in it. I’d rather not have 2nd yield and have things a bit simpler in that space...
On 18 Jul 2023, at 17:06, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, 18 Jul 2023 at 23:41, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
2. Comparing floats, even against zero, is a bad idea. So I would wrap them in math.isclose:
val = a ? (not isclose(c, 0) ? c : d) : (not isclose(d, 0) ? d : c)
That's overly simplistic; there are a lot of times when it's absolutely fine to compare floats for equality, particularly with zero, where the idea of "close" can be ill-defined. But this is the difficulty with toy examples - we have no idea how realistic this actually is. For now, though, I'd be inclined to keep the semantics unchanged and just look at the syntax.
In general you will have function calls adding parentheses -- and thus confusion. Perhaps there will be lists or tuples involved. You can precompute them and assign them to short name variables, but then you lose the one-liner-ness.
This is true, almost trivially so; one-liners aren't all that common because real-world use-cases are often complicated. However, they DO exist. It'd be nice to have really good examples but we may well not have that luxury.
3. OK, how about ints then? Yes, that would work, but how frequently are you going to be comparing against 0? Here's a more realistic case, with readable short argument names:
def clamp_int(n: int, lo: int, hi: int): # I wouldn't elide this test because if lo > hi you get a # plausible but necessarily erroneous value if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') val = n < lo ? lo : (n > hi ? hi : n) return val
Agreed, the expression using Python's syntax would be worse, but I think this is much more readable:
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') if n < lo: val = lo elif n > hi: val = hi else: val = n return val
and it would be more readable yet if you got rid of the assignments and just returned the value as soon as you see it:
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') # Hi, David! Default first!! if lo <= n <= hi: # Yes, Virginia, valid Python! return n elif n > hi: return hi else: return lo
(Yes, I know that some folks argue that suites should have one entry point and one exit point, but I don't think it's a problem here because of the extreme symmetry and simplicity of the conditional. IMHO YMMV of course)
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'{lo = } > {hi = }') return max(lo, min(n, hi))
Nobody said we weren't allowed to use builtins, right? :)
Now, you can add a new one that does the same thing, and that's been done. IIRC it took a while to get +=
Note that augmented operators are not simply shorthands for the expanded version. There are a few differences between:
a.b.c.d.e += value
and
a.b.c.d.e = a.b.c.d.e + value
including that the basis object (a.b.c.d) would only be evaluated once, and especially, the addition is done in-place if supported (eg with lists).
and C-like ++/-- increment operators have been requested over and over again. AFAIK the async syntaxes do nothing that can't be done with generators, but they make it a lot easier to do it right in the most common cases.
A plain async function pretty much IS a generator, but without the risk of removing the last await point and having the function stop being a generator. But async generators are also a thing, and there's no easy way to make a generator-generator with two different types of yield in it.
But yes, in general, these aren't drastically new pieces of functionality - they're nice improvements to readability and expressiveness. I can write an application that uses generators instead of async functions (and my current flagship non-Python project does exactly that), as long as I'm not using generators for anything else. I can write something with no augmented assignment operators, as long as I always use list extend instead of addition, and don't mind the reevaluation of intermediate components (which, most of the time, is only a performance matter, not a correctness one). But it's definitely nice to have them.
ChrisA _______________________________________________ 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/4A2NOU... Code of Conduct: http://python.org/psf/codeofconduct/
Thanks for reply, I can see most of what you have written.
But "encourages one-liners" is generally considered an anti-pattern in Python. Let's see why, Here I am a bit confused, how is this the case in the language containing list comprehensions?
I would understand if it was swapped with “bad quality & unreadable 1-liners”. Also, I would rephrase “encourage 1-liners” to “promote readable and expressive structures that are balanced in their brevity versus complexity”. I am not encouraging 1-liners, I am more arguing that certain things in relation to average complexity should take no more than 1-line. I am always very happy to write multiple lines. E.g. I often use loops instead of list comprehensions: l = list() for i in range(10): l.append(func(i)) These 3 lines justify themselves. A reasonable amount of logic and complexity is contained in them. While I could not say the same about: if a < 1: c = a else: c = default For what it does, it feels it should’t take more than half the space that the “for” loop above is taking. Btw, here I would probably prefer: def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'{lo=} > {hi=}') return lo <= n <= hi ? n : (n > hi ? hi : lo) I emphasize readability more in high-level code, APIs, non-numeric routines and similar. E.g. In numpy code I sacrifice a lot of that “english” readability for aesthetics and brevity, as it is closer to scientific space, where everything is pretty much named with 1-letter + glossary. I think the place I am coming from is more about balance than brevity. In other words, outliers in multi-dimensional spacecan feel awkward to me. Obviously, my dimensions might not be the same as someone else's.
On 18 Jul 2023, at 16:38, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Dom Grigonis writes:
I came to this, because people seem to want one-liners for certain things and what I came up with is that maybe more concise if-else expression could help.
But "encourages one-liners" is generally considered an anti-pattern in Python. Let's see why,
# Fairly reasonable case.
def foo(a: bool, c: float, d: float) val = a ? (c ? c : d) : (d ? d : c) return val
What's not so great here?
1. The argument names are not going to be single characters, unless you intentionally name that way for the sake of one-line-ism. That severely detracts from your claim of readability. The one-liner is arguably[1] more readable than the Python version, but you've definitely made the whole function less readable.
2. Comparing floats, even against zero, is a bad idea. So I would wrap them in math.isclose:
val = a ? (not isclose(c, 0) ? c : d) : (not isclose(d, 0) ? d : c)
Pretty long, not that easy to read. Of course if you had intelligible variable names it would be worse.
The point is *not* to give you a hard time about comparing floats for equality, we've all done that. It's that in even in this example, done safely you have more complex expressions than just variable references. In general you will have function calls adding parentheses -- and thus confusion. Perhaps there will be lists or tuples involved. You can precompute them and assign them to short name variables, but then you lose the one-liner-ness.
3. OK, how about ints then? Yes, that would work, but how frequently are you going to be comparing against 0? Here's a more realistic case, with readable short argument names:
def clamp_int(n: int, lo: int, hi: int): # I wouldn't elide this test because if lo > hi you get a # plausible but necessarily erroneous value if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') val = n < lo ? lo : (n > hi ? hi : n) return val
Agreed, the expression using Python's syntax would be worse, but I think this is much more readable:
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') if n < lo: val = lo elif n > hi: val = hi else: val = n return val
and it would be more readable yet if you got rid of the assignments and just returned the value as soon as you see it:
def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'lo = {lo} > {hi} = hi') # Hi, David! Default first!! if lo <= n <= hi: # Yes, Virginia, valid Python! return n elif n > hi: return hi else: return lo
(Yes, I know that some folks argue that suites should have one entry point and one exit point, but I don't think it's a problem here because of the extreme symmetry and simplicity of the conditional. IMHO YMMV of course)
I dare you to do a 1-liner with current if-else.
I do use ternary expressions occasionally, but almost never nested. Writing one-liners is never a goal for me, in part because my old eyes can't take in more than 35-40 characters in a gulp.
So maybe, just maybe, making already existing expression more readable can also be a valid suggestion?
We *won't do that*, because of backward compatibility. The syntax of an exist expression cannot change without invalidating a lot of existing code. That's one reason why the bar to new syntax is so high, and it took so long to get the ternary expression: you really want to get it right. That's why Python (like C!) prefers to add to the stdlib rather than the language.
Now, you can add a new one that does the same thing, and that's been done. IIRC it took a while to get +=, and C-like ++/-- increment operators have been requested over and over again. AFAIK the async syntaxes do nothing that can't be done with generators, but they make it a lot easier to do it right in the most common cases.
As I said, in my opinion it would solve many existing queries and requests, just because certain things would become very pleasant and obvious how to do in simple one-liners.
One-liners are almost by definition less readable. Even in math papers we break equations into a series of definitions and then build up the final equation from there. As M. Spivak put it in *Calculus on Manifolds*,
Stokes' theorem shares three important attributes with many fully evolved major theorems: 1. It is trivial. 2. It is trivial because the terms appearing in it have been properly defined. 3. It has significant consequences.
When I have the time and knowledge, I aspire to programming that way. :-) YMMV
Simpler and more logically convenient if-else combined with other elegant python expressions would potentially fill that gap.
I guess. It's certainly true that Python pre-walrus operator had no good solution to the "loop and a half" problem. But I've been programming long enough it's hard to get myself to use the walrus operator in the "assign and return" fashion that C's assignment operator so frequently is.
From where I currently stand it just seems fairly happy middle solution between: very concise narrow functionality requests and very verbose ways of how they need to be done at the moment.
But the functionality you request is extremely narrow in a different sense. At least the way people who develop Python (and GNU Mailman, where I program Python in anger, as they say) taught me, very frequently the most concise expression is not the most readable, and "readability counts". So as above I would likely exand these expressions into statement syntax in many cases anyway, despite having a more concise syntax.
I fully appreciate how likely what I am proposing is going to be even considered. But I really think it should be.
I think you will find almost noone in Python development who would agree that it *should* be. Even if you 100% discount the "20 years late" objection that stops it before it gest started.
A lot of the arguments you make would be good arguments, or would have the form of good arguments, for other features. Your "sense" of language development is good, IMO. But if you want to contribute to Python development you need to choose the features to support more in line with conventional wisdom. For example, Chris A's PEP on default argument processing (is that the PEP 671 mentioned earlier?) would resolve what many consider to be a wart, and it hasn't been rejected, just postponed. Or you could choose one that is not merely duplicating existing functionality with syntax ore to your taste.
Steve
On Wed, 19 Jul 2023 at 00:55, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Here I am a bit confused, how is this the case in the language containing list comprehensions?
Do you understand the difference between expressiveness and terseness? You still seem to be focused on the completely wrong thing here. List comprehensions are not about saving lines, they are about expressing the concept of "build a list from a list". ChrisA
To me, they are in the inverse relationship. Terseness ~ 1 / expressiveness. Maybe I am not getting something. But surely if list comprehension took 5 lines to write, it wouldn’t be justifiable? So, even if implicit, brevity and its relation to complexity is always a factor.
On 18 Jul 2023, at 17:57, Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 19 Jul 2023 at 00:55, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Here I am a bit confused, how is this the case in the language containing list comprehensions?
Do you understand the difference between expressiveness and terseness? You still seem to be focused on the completely wrong thing here. List comprehensions are not about saving lines, they are about expressing the concept of "build a list from a list".
ChrisA _______________________________________________ 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/HES5J5... Code of Conduct: http://python.org/psf/codeofconduct/
To me, saying it’s about expressiveness but not brevity sounds similar to e.g. “we are in compliance with green policies of EU, but we are not making effort to save nature.” Well, you might not consciously intend to save nature, but those who gave you presentation about green legislation had that intent, but used fancy words so it sounds cooler. Well, saving nature is at least one of major factors, other’s being someone profiting from it, other political implications etc... I might be completely wrong here, some simple and clear emphasis of how they are unrelated is welcome.
On 18 Jul 2023, at 18:11, Dom Grigonis <dom.grigonis@gmail.com> wrote:
To me, they are in the inverse relationship.
Terseness ~ 1 / expressiveness.
Maybe I am not getting something.
But surely if list comprehension took 5 lines to write, it wouldn’t be justifiable? So, even if implicit, brevity and its relation to complexity is always a factor.
On 18 Jul 2023, at 17:57, Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 19 Jul 2023 at 00:55, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Here I am a bit confused, how is this the case in the language containing list comprehensions?
Do you understand the difference between expressiveness and terseness? You still seem to be focused on the completely wrong thing here. List comprehensions are not about saving lines, they are about expressing the concept of "build a list from a list".
ChrisA _______________________________________________ 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/HES5J5... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 18 Jul 2023 at 16:13, Dom Grigonis <dom.grigonis@gmail.com> wrote:
To me, they are in the inverse relationship.
Terseness ~ 1 / expressiveness.
No, they are unrelated (in general). Terseness is nothing more than "can I use fewer characters?" and is almost always the wrong thing to do in isolation (yes, if things get *yoo* verbose they can be hard to comprehend, but that's about going to extremes, not about terseness as such). Expressiveness is about matching the *concepts* involved to the problem. So list comprehensions are expressive because they declaratively say what the content of the list should be, and avoid the unnecessary concepts around how you build the list. It's irrelevant how many characters are involved, what matters is that you can omit saying "first create an empty list, now add elements like <this> one by one..." Paul
When you put it from this perspective, it seems to make sense, however, I do not agree. To me, from first principles it seems it’s all about condensing more complexity in smaller modular packages. Leaving modularity aside, condensing more complexity in smaller packages in programming area is most often expressed via abstractions. Starting from binary we build more expressive components and applications, where each layer uses less and less code to do more and more complex things. Yes, when you increase complexity without increasing the size - you say expressiveness. But when you make package smaller without increasing complexity - you say conciseness. So "expressiveness ~ 1/conciseness" if both aspects of phenomena are taken into account. And decreasing number of characters is not conciseness, but rather 1 dimension of it, nevertheless important. That is why I am a bit confused, when someone argues that it is not about conciseness, but about expressiveness. I see the POV, that say ok x-size, y-expressiveness, however I think in this case, it is not expressiveness, but rather functionality. So x-size(however one defines it), y-functionality. Expressiveness = y/x Conciseness = x/y Thus, expressiveness = 1 / conciseness. Maybe I am not using the terminology correctly (seems right to me, given etymology of these words), but this is just to explain what I meant.
On 18 Jul 2023, at 18:34, Paul Moore <p.f.moore@gmail.com> wrote:
On Tue, 18 Jul 2023 at 16:13, Dom Grigonis <dom.grigonis@gmail.com <mailto:dom.grigonis@gmail.com>> wrote: To me, they are in the inverse relationship.
Terseness ~ 1 / expressiveness.
No, they are unrelated (in general).
Terseness is nothing more than "can I use fewer characters?" and is almost always the wrong thing to do in isolation (yes, if things get *yoo* verbose they can be hard to comprehend, but that's about going to extremes, not about terseness as such).
Expressiveness is about matching the *concepts* involved to the problem. So list comprehensions are expressive because they declaratively say what the content of the list should be, and avoid the unnecessary concepts around how you build the list. It's irrelevant how many characters are involved, what matters is that you can omit saying "first create an empty list, now add elements like <this> one by one..."
Paul
On Wed, 19 Jul 2023 at 06:41, Dom Grigonis <dom.grigonis@gmail.com> wrote:
When you put it from this perspective, it seems to make sense, however, I do not agree.
To me, from first principles it seems it’s all about condensing more complexity in smaller modular packages.
That is why I am a bit confused, when someone argues that it is not about conciseness, but about expressiveness.
You're confused, yet you refuse to accept that you might be wrong. I don't think there's any point me continuing to discuss this with you. ChrisA
Ok, fair I am confusing definitions. Expressiveness, defined by modern dictionary: “the quality of effectively conveying a thought or feeling”. Let me correct myself. Although I see that expressiveness is important, I think variable: Functionality / verbosity - can be as important.
On 18 Jul 2023, at 23:59, Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 19 Jul 2023 at 06:41, Dom Grigonis <dom.grigonis@gmail.com> wrote:
When you put it from this perspective, it seems to make sense, however, I do not agree.
To me, from first principles it seems it’s all about condensing more complexity in smaller modular packages.
That is why I am a bit confused, when someone argues that it is not about conciseness, but about expressiveness.
You're confused, yet you refuse to accept that you might be wrong. I don't think there's any point me continuing to discuss this with you.
ChrisA _______________________________________________ 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/M76SNA... Code of Conduct: http://python.org/psf/codeofconduct/
Dom Grigonis writes:
But "encourages one-liners" is generally considered an anti-pattern in Python. Let's see why,
Here I am a bit confused, how is this the case in the language containing list comprehensions?
I don't think of list comprehensions in terms of line count. I often write multiple-line list comprehensions and genexps. Especially when nested or with an 'if' clause,, I often use multiple lines even though the whole thing would fit on a single line because I feel it expresses the structure better. Comprehensions and genexps are an especially nice context for that, because they are always equipped with parentheses, so you are not constrained by the usual rules of indentation, and don't need line continuing backslashes. Also consider for x in list1: list2.append(foo(x)) list2.extend([foo(x) for x in list1]) The second form is one character less concise than the former, yet far more expressive. Once genexps were introduced, we could make it one character more concise than the for loop with list2.extend(foo(x) for x in list1) but it's unclear that this is an improvement over the list comprehension. (That's probably due to the facts that I don't use genexps that often, and that I think of list.extend as a concatenation of lists even though it's documented as taking an iterable. If most people have more facility with genexps than I do, it's a small but clear improvement.)
I would understand if it was swapped with “bad quality & unreadable 1-liners”.
That dodges the question of when does a one-liner cross over to "bad quality and unreadable", though.
Also, I would rephrase “encourage 1-liners” to “promote readable and expressive structures that are balanced in their brevity versus complexity”. I am not encouraging 1-liners,
Well, the word "one-liner" has a history. "One-liner" is a category by that name in the Obfuscated C Contest, and Perl programmers often take pride in how much they can accomplish on the command line, without starting an editor or interactive interpreter. Some of us old-timers are going to take it the wrong way. I'm not sure if less indoctrinated people would take it to mean "readable and expressive structures that are balanced in their brevity versus complexity". :-) In any case, whether you intend it or not, making the ternary expression more terse would encourage examples of the kind you present.
I am more arguing that certain things in relation to average complexity should take no more than 1-line. I am always very happy to write multiple lines.
A Python ternary expression usually takes only part of one line. They allow you to put a simple conditional in the middle of a longer expression. However, they're not amenable to nesting ternaries, except in very special circumstances. I think that's a good thing, you don't. We can both be right, you know! I'm just trying to explain why I think that way, and making the claim (which may be incorrect) that the Pythonistas who influence language design do, too.
Btw, here I would probably prefer: def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'{lo=} > {hi=}') return lo <= n <= hi ? n : (n > hi ? hi : lo)
To my eye, that's a great example of "concise but not expressive". Parsing it requires reading almost character by character, the nuance of the conditional changes from "True => usual case" to a genuine choice, and verifying the correctness of the expression is nontrivial compared to verifying the if statement form.
I think the place I am coming from is more about balance than brevity.
OK. But for me that's hard to see when it's expressed by counting lines. Balance and nuance sometimes can be expressed in words, but in cases where the line is hard to draw, we often go through a large corpus and find as many examples as possible and compare the existing code with versions using the new syntax. Typically it's the stdlib, but since you mention numerical analysis, numpy or something based on it like pandas might provide better examples for you. I don't recommend that here. The existence of a semantically equivalent ternary expression already makes that an impossible lift. Steve
I understand. As I have already said, I am not exactly proposing more terse if-else expression. It’s just where I got to going down the path of several PEPs, proposals and my own 'struggles’. My point was more along the lines, that these are the examples, where several users and PEP writers think python can be improved. It looked like, having more terse ternary expression would get all of these half the way in certain dimensions that they improve on. I see that dimensions that I picked up upon were not exactly the same ones that were aimed at (at least in PEPs). What I was aiming at is getting more information and ideas and tried to pin point common root between all of them, which seemed to be the same which I, myself is battling with, but can't pin-point. Although, I do no deny, I got a bit sidetracked with ternary expression. :) Verbosity, although important to me, but is of secondary importance. Well, at least in ternary expression itself and number of characters. But, when a simple thing such as dealing with default of 1 argument takes 4 lines of code, it does bother me. —————————— In reality, I do not think changing ternary expression would be worth the buck anyway. The thing I am not content with (to make it common practice in my coding style) is inconsistencies between a) order of terms and b) functionality, of statements and their analogous expressions. If they were consistent, in a similar way that I wrote in a previous e-mail, it might do the trick. But even then it wouldn’t cover all the cases that I am looking at. It seems to be a bit wider. —————————— `Deferred evaluation`, if was to gain traction in a similar manner as e.g. `annotations` are now experiencing, would cover all of the cases I was looking at & more. Maybe it would be a win-win. At the core, python would retain its expressiveness (which to me personally is more than sufficient compared to other dimensions that are important in the context of how I use python and where I aim to get with it), but there would exist "a way" for those who want/need to write very logically consistent, optimised and concise code. Thank you for reply, DG
On 19 Jul 2023, at 09:25, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Dom Grigonis writes:
But "encourages one-liners" is generally considered an anti-pattern in Python. Let's see why,
Here I am a bit confused, how is this the case in the language containing list comprehensions?
I don't think of list comprehensions in terms of line count. I often write multiple-line list comprehensions and genexps. Especially when nested or with an 'if' clause,, I often use multiple lines even though the whole thing would fit on a single line because I feel it expresses the structure better. Comprehensions and genexps are an especially nice context for that, because they are always equipped with parentheses, so you are not constrained by the usual rules of indentation, and don't need line continuing backslashes.
Also consider
for x in list1: list2.append(foo(x))
list2.extend([foo(x) for x in list1])
The second form is one character less concise than the former, yet far more expressive. Once genexps were introduced, we could make it one character more concise than the for loop with
list2.extend(foo(x) for x in list1)
but it's unclear that this is an improvement over the list comprehension. (That's probably due to the facts that I don't use genexps that often, and that I think of list.extend as a concatenation of lists even though it's documented as taking an iterable. If most people have more facility with genexps than I do, it's a small but clear improvement.)
I would understand if it was swapped with “bad quality & unreadable 1-liners”.
That dodges the question of when does a one-liner cross over to "bad quality and unreadable", though.
Also, I would rephrase “encourage 1-liners” to “promote readable and expressive structures that are balanced in their brevity versus complexity”. I am not encouraging 1-liners,
Well, the word "one-liner" has a history. "One-liner" is a category by that name in the Obfuscated C Contest, and Perl programmers often take pride in how much they can accomplish on the command line, without starting an editor or interactive interpreter. Some of us old-timers are going to take it the wrong way. I'm not sure if less indoctrinated people would take it to mean "readable and expressive structures that are balanced in their brevity versus complexity". :-)
In any case, whether you intend it or not, making the ternary expression more terse would encourage examples of the kind you present.
I am more arguing that certain things in relation to average complexity should take no more than 1-line. I am always very happy to write multiple lines.
A Python ternary expression usually takes only part of one line. They allow you to put a simple conditional in the middle of a longer expression. However, they're not amenable to nesting ternaries, except in very special circumstances. I think that's a good thing, you don't. We can both be right, you know! I'm just trying to explain why I think that way, and making the claim (which may be incorrect) that the Pythonistas who influence language design do, too.
Btw, here I would probably prefer: def clamp_int(n: int, lo: int, hi: int): if lo > hi: raise ValueError(f'{lo=} > {hi=}') return lo <= n <= hi ? n : (n > hi ? hi : lo)
To my eye, that's a great example of "concise but not expressive". Parsing it requires reading almost character by character, the nuance of the conditional changes from "True => usual case" to a genuine choice, and verifying the correctness of the expression is nontrivial compared to verifying the if statement form.
I think the place I am coming from is more about balance than brevity.
OK. But for me that's hard to see when it's expressed by counting lines.
Balance and nuance sometimes can be expressed in words, but in cases where the line is hard to draw, we often go through a large corpus and find as many examples as possible and compare the existing code with versions using the new syntax. Typically it's the stdlib, but since you mention numerical analysis, numpy or something based on it like pandas might provide better examples for you. I don't recommend that here. The existence of a semantically equivalent ternary expression already makes that an impossible lift.
Steve
Dom Grigonis writes:
The thing I am not content with (to make it common practice in my coding style) is inconsistencies between a) order of terms and b) functionality, of statements and their analogous expressions.
I think that's reasonable. But I don't think Python is the language for that. There are too much existing contrasts, such as loop vs. comprehension and conditional statement vs. ternary expression. Guido deliberately chose to vary those expression syntaxes from their statement equivalents. I don't think there's any ambiguity if you say that wrapping a compound statement in parentheses creates an expression with restricted syntax (namely, the statements the syntax controls become expressions): (if condition_1: expression_1 elif condition_2: expression_2 else: expression_else) [for element in iterable: if condition: expression(element)] But Guido (and I think everybody else) thought "Noooo! not *that*!"
?????????? `Deferred evaluation`, if was to gain traction in a similar manner as e.g. `annotations` are now experiencing, would cover all of the cases I was looking at & more.
As Chris was saying earlier, it's at minimum going to take some genius creativity to "cover all and more", because a closer look at what people mean by "deferred" shows that in different cases it is semantically different. In particular, there are a number of choices that Chris made in PEP 671 that aren't compatible with several of the proposals for Deferred objectss, while Deferreds can't give some of the benefits of the PEP. I didn't like that PEP then; I was in the camp of "let's get genuine Deferreds, and use them to cover some of the use cases for PEP 671." I don't actively want the PEP now. def-time evaluation of defaults doesn't catch me and I haven't had trouble teaching it. The "if arg is None: arg = Mutable()" idiom is rare enough that I prefer it to adding syntax to the already complex function prototype. But if you think it might be a good idea, I encourage you to take a close look. For some use cases it's definitely an improvement, and at least I won't vocally oppose the PEP now -- haven't seen any progress on "true Deferreds". Perhaps others' opposition has softened, too. Adding another proponent is another way to help get it going again. Steve
I think that's reasonable. But I don't think Python is the language for that. There are too much existing contrasts, such as loop vs. comprehension and conditional statement vs. ternary expression. Guido deliberately chose to vary those expression syntaxes from their statement equivalents. I don't think there's any ambiguity if you say that wrapping a compound statement in parentheses creates an expression with restricted syntax (namely, the statements the syntax controls become expressions):
(if condition_1: expression_1 elif condition_2: expression_2 else: expression_else)
[for element in iterable: if condition: expression(element)]
But Guido (and I think everybody else) thought "Noooo! not *that*!”
Yes, I realise that there is no way back as different direction has already been taken. So I am not (and have not been) proposing any changes there, just trying to find ways to get to where I would like to with python.
?????????? `Deferred evaluation`, if was to gain traction in a similar manner as e.g. `annotations` are now experiencing, would cover all of the cases I was looking at & more.
As Chris was saying earlier, it's at minimum going to take some genius creativity to "cover all and more", because a closer look at what people mean by "deferred" shows that in different cases it is semantically different. In particular, there are a number of choices that Chris made in PEP 671 that aren't compatible with several of the proposals for Deferred objectss, while Deferreds can't give some of the benefits of the PEP.
I have read both PEP671 and https://github.com/DavidMertz/peps/blob/master/pep-9999.rst <https://github.com/DavidMertz/peps/blob/master/pep-9999.rst>, but can’t see what benefits does PEP provide, that def-eval can not. I see that they are not the same in implementation even orthogonal, but it seems that PEP671, maybe not as elegantly, but can achieve the same result.
I didn't like that PEP then; I was in the camp of "let's get genuine Deferreds, and use them to cover some of the use cases for PEP 671." I don't actively want the PEP now. def-time evaluation of defaults doesn't catch me and I haven't had trouble teaching it. The "if arg is None: arg = Mutable()" idiom is rare enough that I prefer it to adding syntax to the already complex function prototype.
That idiom is only 1 example. See my e-mail with examples of how def-eval relates to several PEPs and requests that came up recently in this group. It would improve things in many areas. E.g. infix operators might become an actually useful thing instead of being just a cool thing to take a look at. IMO, infix pattern is too cumbersome to be used as actual operator, but would be perfect for defining binary and maybe ternary expressions. If it’s usage would be as in “def-eval rst", it would potentially bring about a new paradigm to the way I do things. Especially if it was well optimised and had a concise syntax. # something similar to `expr` # or ?(expr)
But if you think it might be a good idea, I encourage you to take a close look. For some use cases it's definitely an improvement, and at least I won't vocally oppose the PEP now -- haven't seen any progress on "true Deferreds". Perhaps others' opposition has softened, too. Adding another proponent is another way to help get it going again.
I am not sure if I would be the right person for this, but if you know any good places (discussions or attempts) to look at apart from that “def-eval rst”, I would take a look. So far I am only excited about it from user’s perspective… DG
On Thu, 20 Jul 2023 at 15:33, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
I didn't like that PEP then; I was in the camp of "let's get genuine Deferreds, and use them to cover some of the use cases for PEP 671."
You're almost certainly never going to get that, because "genuine Deferreds", as well as being nearly impossible to pin down, can't actually cover all the use-cases of PEP 671. ChrisA
On Thu, 20 Jul 2023 at 22:27, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Chris Angelico writes:
On Thu, 20 Jul 2023 at 15:33, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
C'mon Chris, "that was then, this is now". Catch up, I've changed my position. ;-)
That's fair :) Maybe I'll dust off that proposal again at some point. ChrisA
On Thu, Jul 20, 2023, 01:19 Rob Cliffe via Python-ideas < python-ideas@python.org> wrote:
On 15/07/2023 21:08, Dom Grigonis wrote:
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.
Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Note also that when the condition is a truthiness check -- admittedly not always the case, but it does occur reasonably often -- this can be simplified further to: result = bar or default
On 20/07/23 6:30 pm, James Addison via Python-ideas wrote:
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Would it shut anyone up if we had another logical operator: x otherwise y equivalent to x if x is not None else y ? -- Greg
On Thu, 20 Jul 2023 at 18:16, Greg Ewing <gcewing@snap.net.nz> wrote:
On 20/07/23 6:30 pm, James Addison via Python-ideas wrote:
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Would it shut anyone up if we had another logical operator:
x otherwise y
equivalent to
x if x is not None else y
?
I have looked more properly at deferred evaluation and late-bound defaults. Deferred-eval is overkill for what I need and isn’t likely to come about soon, while deferred only solves a very small part of what I am looking at. Also, it lacks ability to enforce default from outside, which is a bit of a dealbreaker to me. Sooo… I have come up to a conclusion that what would shut me up is a shorthand syntax for lambda. Either: 1) Ability to assign it to a different name or MUCH MORE preferably 2) lambda: expr() == `expr()` == ?(expr()) == ???. Anything concise and sensible would do. lambda pretty much does the trick, but syntax is too awkward to use for such applications. And ideally, adding 2 builtin(or imported from stdlib) functions: `Break` and `Continue`, which if called act as `break` and `continue` statements.
On 20 Jul 2023, at 11:11, Greg Ewing <gcewing@snap.net.nz> wrote:
On 20/07/23 6:30 pm, James Addison via Python-ideas wrote:
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Would it shut anyone up if we had another logical operator:
x otherwise y
equivalent to
x if x is not None else y
?
-- Greg
_______________________________________________ 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/R55FL6... Code of Conduct: http://python.org/psf/codeofconduct/
On Thu, 20 Jul 2023 at 19:34, Dom Grigonis <dom.grigonis@gmail.com> wrote:
And ideally, adding 2 builtin(or imported from stdlib) functions: `Break` and `Continue`, which if called act as `break` and `continue` statements.
How would they work? Would every function call have to provide the possibility to return to a location that wasn't where you would otherwise go? if random.randrange(2): func = Break else: def func(): pass def wut(): for i in range(1, 10): if i % 3: func() print(i) wut() Please, tell me how this would behave. ChrisA
Ok, this is tricky. So pretty much impossible, given that `Break`, if it was to work has to be evaluated in reference scope, while other delayed objects in their definition scope. ---------------------- Back to deferred evaluation. ---------------------- Giving it a rest for a while. Thank you for your patience.
On 20 Jul 2023, at 12:38, Chris Angelico <rosuav@gmail.com> wrote:
On Thu, 20 Jul 2023 at 19:34, Dom Grigonis <dom.grigonis@gmail.com> wrote:
And ideally, adding 2 builtin(or imported from stdlib) functions: `Break` and `Continue`, which if called act as `break` and `continue` statements.
How would they work? Would every function call have to provide the possibility to return to a location that wasn't where you would otherwise go?
if random.randrange(2): func = Break else: def func(): pass
def wut(): for i in range(1, 10): if i % 3: func() print(i)
wut()
Please, tell me how this would behave.
ChrisA
The challenge with anything like result = default if bar is None else bar or result = bar if bar else default or even result = bar is None ? default : bar is that sure default is only evaluated once - but in all cases 'bar' is evaluated twice, and if bar is a function call then no parser is clever enough to diagnose all possible side effects of calling bar() twice, or even detect that bar can be cached, so it will always be called twice. In Python then a better way might be result = temp := bar() if temp else default This way only bar() and default are evaluated and invoked once. ------ Original Message ------ From: "Rob Cliffe via Python-ideas" <python-ideas@python.org> To: "Dom Grigonis" <dom.grigonis@gmail.com>; "Jothir Adithyan" <adithyanjothir@gmail.com> Cc: python-ideas@python.org Sent: Monday, 17 Jul, 23 At 16:09 Subject: [Python-ideas] Re: Proposal for get_or function in Python dictionaries On 15/07/2023 21:08, Dom Grigonis wrote: 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. Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent result = default if bar is None else bar or if you prefer result = bar if bar is not None else default Perhaps you didn't know about this construction? It does exactly the same as the C version and is more readable. Or am I missing something? Best wishes Rob Cliffe _______________________________________________ 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/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4QBAYB... <https://mail.python.org/archives/list/python-ideas@python.org/message/4QBAYBAT6EZFILNS2MCK3D6XW4LNRDZ5/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/> -- <br>Anthony Flury<br>anthony.flury@btinternet.com
Thanks, thats truly useful to keep in mind.
On 20 Jul 2023, at 09:48, anthony.flury <anthony.flury@btinternet.com> wrote:
The challenge with anything like
result = default if bar is None else bar
or result = bar if bar else default
or even
result = bar is None ? default : bar
is that sure default is only evaluated once - but in all cases 'bar' is evaluated twice, and if bar is a function call then no parser is clever enough to diagnose all possible side effects of calling bar() twice, or even detect that bar can be cached, so it will always be called twice.
In Python then a better way might be
result = temp := bar() if temp else default
This way only bar() and default are evaluated and invoked once.
------ Original Message ------ From: "Rob Cliffe via Python-ideas" <python-ideas@python.org <mailto:python-ideas@python.org>> To: "Dom Grigonis" <dom.grigonis@gmail.com <mailto:dom.grigonis@gmail.com>>; "Jothir Adithyan" <adithyanjothir@gmail.com <mailto:adithyanjothir@gmail.com>> Cc: python-ideas@python.org <mailto:python-ideas@python.org> Sent: Monday, 17 Jul, 23 At 16:09 Subject: [Python-ideas] Re: Proposal for get_or function in Python dictionaries
On 15/07/2023 21:08, Dom Grigonis wrote:
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.
Well, default is only evaluated if needed; bar is always evaluated. What is wrong with the Python equivalent
result = default if bar is None else bar or if you prefer result = bar if bar is not None else default
Perhaps you didn't know about this construction? It does exactly the same as the C version and is more readable. Or am I missing something? Best wishes Rob Cliffe
_______________________________________________
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/4QBAYB... <https://mail.python.org/archives/list/python-ideas@python.org/message/4QBAYBAT6EZFILNS2MCK3D6XW4LNRDZ5/>
Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
-- <br>Anthony Flury<br>anthony.flury@btinternet.com <mailto:anthony.flury@btinternet.com>
On Thu, 20 Jul 2023 at 17:09, Dom Grigonis <dom.grigonis@gmail.com> wrote:
On 20 Jul 2023, at 09:48, anthony.flury <anthony.flury@btinternet.com> wrote: In Python then a better way might be
result = temp := bar() if temp else default
This way only bar() and default are evaluated and invoked once.
I presume you want a more complicated expression than just "if temp", since this is no better than "bar() or default". But if you DO want some other condition, this would be how you'd write it: result = temp if (temp := bar()) is None else default Or, better: result = bar() if result is None: result = default ChrisA
On Mon, 24 Jul 2023 at 10:31, anthony.flury via Python-ideas < python-ideas@python.org> wrote:
In Python then a better way might be
result = temp := bar() if temp else default
This way only bar() and default are evaluated and invoked once.
Or result = bar() if not result: result = default Seriously, what is the obsession with putting things on one line here? If it's because "it works better when there is a long list of such assignments" then unless you actually show a real-world example, it's hard to discuss the *actual* trade-offs. And typically with a real-world example, there are a lot more options available (for example, if this is defaulting a series of arguments to a function, it's often an anti-pattern to have a function with that many arguments in the first place, so refactoring the code may be a better option). Paul
Seriously, what is the obsession with putting things on one line here?
There is no obsession. It is more of a consideration of balance between operation complexity vs its verbosity.
If it's because "it works better when there is a long list of such assignments" then unless you actually show a real-world example, it's hard to discuss the *actual* trade-offs.
First library that comes to mind, which has a high level constructor: https://github.com/pydata/xarray/blob/main/xarray/core/dataarray.py#L437 <https://github.com/pydata/xarray/blob/main/xarray/core/dataarray.py#L437>
It's often an anti-pattern to have a function with that many arguments in the first place, so refactoring the code may be a better option). It often is, but at the same time unavoidable in some cases, i.e. high level APIs. Few examples, CLIs (e.g. poetry), constructors of high level objects.
I think what would be nice to have is a set of dimensions that are deemed to be of importance when deciding on a language constructs. plot(dim1, dim2, dim3) dim1 - number of lines dim2 - number of terms dim3 - number of operations dim4 - frequency of usage dim5 - ... Of course, not all dimensions can be quantified. And to quantify some of them certain definitions would have to be decided on, e.g. what is an operation. Also, I do not know what those dimensions should be, they would ideally be decided by those who have a very good sense of python’s long-term vision. Such information could be helpful in having productive discussions regarding such considerations and proposals. In this case I am concerned with a balance of brevity vs complexity of python constructs, which is a core building block in a space of constantly increasing number of abstraction layers. —Nothing ever dies, just enters the state of deferred evaluation— DG
On Tue, 25 Jul 2023 at 01:03, Dom Grigonis <dom.grigonis@gmail.com> wrote:
I think what would be nice to have is a set of dimensions that are deemed to be of importance when deciding on a language constructs. plot(dim1, dim2, dim3) dim1 - number of lines dim2 - number of terms dim3 - number of operations dim4 - frequency of usage dim5 - ...
Don't think I fully understand, but aren't three out of those four just different forms of brevity? Is that really so important to you? ChrisA
2 of them are forms of brevity. To me the whole picture is important. And the context as well. Individual aspect becomes more important when it feels to be an outlier. I.e. In 1 line one can create a dictionary, initialise it with reasonable keys and 2-values tuples and find the key of the maximum of tuple sums… Elegantly. But one needs 3 lines of code to resolve default value. This is an exaggeration, but serves to convey my point. But again, this is not about this individual case. This thread has started from new dict.get method, and not by me. I am just trying to see what is the common factor as similar problems have been bothering me too. To me by now, it is about custom user expressions. —Nothing ever dies, just enters the state of deferred evaluation— Dg
On 24 Jul 2023, at 18:17, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, 25 Jul 2023 at 01:03, Dom Grigonis <dom.grigonis@gmail.com> wrote:
I think what would be nice to have is a set of dimensions that are deemed to be of importance when deciding on a language constructs. plot(dim1, dim2, dim3) dim1 - number of lines dim2 - number of terms dim3 - number of operations dim4 - frequency of usage dim5 - ...
Don't think I fully understand, but aren't three out of those four just different forms of brevity? Is that really so important to you?
ChrisA _______________________________________________ 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/6RF4PM... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 11 Jul 2023 at 08:05, Jothir Adithyan <adithyanjothir@gmail.com> wrote:
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.
The main assumption here is that None is a perfectly valid value in your data, and should be equivalent to the key being omitted. I think this is uncommon enough to be better done with custom code. But fortunately, there's a really cool way to write that custom code, and that's a path lookup - this is a sort of function that I've written in a lot of these situations. Using your examples:
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
I'd write it like this: def lookup(basis, *keys, default=None): for key in keys: basis = basis.get(key) if basis is None: return default return basis result = lookup(parent_dict, "k1", "child_key") This quite tidily expresses the intent ("delve into this dictionary and follow this path, returning None if there's nothing there"), and is compact, with no repetition. It's a relatively short helper function too, and easy to change into other forms. For example, here's another version that I use, which guarantees to always return a dictionary: def lookup(basis, *keys): for key in keys: if key not in basis: basis[key] = {} basis = basis[key] return basis Etcetera. Flexible and very handy. ChrisA
Chris Angelico wrote:
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.
The main assumption here is that None is a perfectly valid value in your data, and should be equivalent to the key being omitted. I think
On Tue, 11 Jul 2023 at 08:05, Jothir Adithyan adithyanjothir@gmail.com wrote: this is uncommon enough to be better done with custom code. But fortunately, there's a really cool way to write that custom code, and that's a path lookup - this is a sort of function that I've written in a lot of these situations. Using your examples:
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 I'd write it like this: def lookup(basis, *keys, default=None): for key in keys: basis = basis.get(key) if basis is None: return default return basis result = lookup(parent_dict, "k1", "child_key") This quite tidily expresses the intent ("delve into this dictionary and follow this path, returning None if there's nothing there"), and is compact, with no repetition. It's a relatively short helper function too, and easy to change into other forms. For example, here's another version that I use, which guarantees to always return a dictionary: def lookup(basis, *keys): for key in keys: if key not in basis: basis[key] = {} basis = basis[key] return basis Etcetera. Flexible and very handy. ChrisA Hey Chris, thank you for your time and insight.
I've thought about this for some time, and I believe that adding this feature could indeed be a useful and concise helper for many Python users, especially for API integrations and web scraping where encountering None values is quite common. Do you think it would be feasible to modify the existing get function to include a new parameter or perhaps introduce a new function altogether? I'm trying to understand why this idea may not be considered favorable. What are the reasons for not having this feature? What makes the current approach better than having this additional functionality?
On Sun, 16 Jul 2023 at 04:07, Jothir Adithyan <adithyanjothir@gmail.com> wrote:
Chris Angelico wrote:
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.
The main assumption here is that None is a perfectly valid value in your data, and should be equivalent to the key being omitted. I think
On Tue, 11 Jul 2023 at 08:05, Jothir Adithyan adithyanjothir@gmail.com wrote: this is uncommon enough to be better done with custom code. But fortunately, there's a really cool way to write that custom code, and that's a path lookup - this is a sort of function that I've written in a lot of these situations. Using your examples:
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 I'd write it like this: def lookup(basis, *keys, default=None): for key in keys: basis = basis.get(key) if basis is None: return default return basis result = lookup(parent_dict, "k1", "child_key") This quite tidily expresses the intent ("delve into this dictionary and follow this path, returning None if there's nothing there"), and is compact, with no repetition. It's a relatively short helper function too, and easy to change into other forms. For example, here's another version that I use, which guarantees to always return a dictionary: def lookup(basis, *keys): for key in keys: if key not in basis: basis[key] = {} basis = basis[key] return basis Etcetera. Flexible and very handy. ChrisA Hey Chris, thank you for your time and insight.
I've thought about this for some time, and I believe that adding this feature could indeed be a useful and concise helper for many Python users, especially for API integrations and web scraping where encountering None values is quite common. Do you think it would be feasible to modify the existing get function to include a new parameter or perhaps introduce a new function altogether?
I'm trying to understand why this idea may not be considered favorable. What are the reasons for not having this feature? What makes the current approach better than having this additional functionality?
The reason it's not a good fit for the language itself is that there are lots of small variations. I gave you one other slight variant in my example, and there are myriad other small differences that could show up. So it's best for it to be part of your own toolkit. I would strongly recommend giving the "path lookup" function style a try; it's actually way more expressive than chained lookups, in the situations that it's appropriate to. ChrisA
This feels superfluous. Instead of creating new dict class I would propose either: 1. Not to have None values a) It is most likely possible to pre-delete all None values before you use the dict = {k: v for k, v in dict if v is not None} b) Not to create them in the first place (if it depends on you) 2. Or simply: (dict.get(‘k1’) or dict()).get(‘child_key')
On 10 Jul 2023, at 22:18, Jothir Adithyan <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
**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 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/IELDCU... Code of Conduct: http://python.org/psf/codeofconduct/
Dom Grigonis wrote:
This feels superfluous. Instead of creating new dict class I would propose either: 1. Not to have None values a) It is most likely possible to pre-delete all None values before you use the dict = {k: v for k, v in dict if v is not None} b) Not to create them in the first place (if it depends on you) 2. Or simply: (dict.get(‘k1’) or dict()).get(‘child_key')
On 10 Jul 2023, at 22:18, Jothir Adithyan 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 **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 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/IELDCU... Code of Conduct: http://python.org/psf/codeofconduct/
I understand your perspective, but the fact is that this makes the code lengthier and adds an extra step to what I am already trying to do. Additionally, if I understand correctly, I could implement a built-in method as a function that takes in the key and uses a try-except block to get the value or return None, which is essentially Python's way of handling missing values (similar to get()). I proposed this solution because Python is widely used by many people and organizations for web scraping and API integrations, where None values are often expected and need to be handled efficiently. I believe this could be a valuable addition, but please correct me if I'm wrong.
I understand your perspective, but the fact is that this makes the code lengthier and adds an extra step to what I am already trying to do. Additionally, if I understand correctly, I could implement a built-in method as a function that takes in the key and uses a try-except block to get the value or return None, which is essentially Python's way of handling missing values (similar to get()). I proposed this solution because Python is widely used by many people and organizations for web scraping and API integrations, where None values are often expected and need to be handled efficiently. I believe this could be a valuable addition, but please correct me if I'm wrong. I think adding an extra method is the only reasonable solution anyways. Modifying `dict.get` even if not breaking any existing functionality, would result in decreased performance across the whole landscape of code that uses `get`, which doesn’t seem like a big cost, but from experience, dictionary methods do tend to become bottlenecks in highly-optimised python code (which is often the case given the obvious disadvantage of interpreted language).
So the only question here is whether to add an extra method in python core. Such as you did e.g. `get_if`, or leave it to the user. If left to the user, then maybe it is possible to solve several issues at the same time by providing elegant, optimised & flexible one-liners (see my other e-mail) to do such thing, leaving implementation details to the user. You are concerned about `None`, maybe someone else is concerned about some other value that he wishes to treat as non-valid. Permutations are endless in value-space and depth. Implementing and especially changing already existing functionality in core-python requires consideration of many perspectives, which I, myself, do not have enough, so this is just my opinion. But from what I have gathered from experience analysing others' and my own suggestions in this group, the one you are proposing (at least taken at it’s face value) is too limiting, and potentially too invasive for too little benefit in relation to how close to `python’s metal` it is.
On 15 Jul 2023, at 20:57, Jothir Adithyan <adithyanjothir@gmail.com> wrote:
Dom Grigonis wrote:
This feels superfluous. Instead of creating new dict class I would propose either: 1. Not to have None values a) It is most likely possible to pre-delete all None values before you use the dict = {k: v for k, v in dict if v is not None} b) Not to create them in the first place (if it depends on you) 2. Or simply: (dict.get(‘k1’) or dict()).get(‘child_key')
On 10 Jul 2023, at 22:18, Jothir Adithyan 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 **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 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/IELDCU... Code of Conduct: 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-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/LWOSXQ... <https://mail.python.org/archives/list/python-ideas@python.org/message/LWOSXQSZKYO6KUKIZCA6ONUDKRKD4N7G/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
participants (14)
-
anthony.flury
-
Barry
-
Chris Angelico
-
Christopher Barker
-
David Mertz, Ph.D.
-
Dom Grigonis
-
Greg Ewing
-
James Addison
-
Jothir Adithyan
-
MRAB
-
Ned Batchelder
-
Paul Moore
-
Rob Cliffe
-
Stephen J. Turnbull