Syntax to conditionally define a field in a dict
From the slack channel #learning_python, there are a number of more general
Hello all, I have a use case where I need to send a `dict` to a module as an argument. Inside of this, it has a multi-level structure, but each field I need to set may only be set to a single value. Fields must be valid, non-empty strings. It looks a lot like the following in my code: ``` def my_func(val_1, val_2): return { "field_1": val_1, "next_depth": { "field_2": val_2 } } ``` What I want to do is: ``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ``` Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } } ``` Where each conditional in this context functions as: ``` if value: d["your_key"] = value ``` for each conditionally added and set key. points which need to be handled. The more core syntax, which should be valid throughout the language, would be to have statements like `x = y if cond` and `x[y if cond]`. The first of these intuitively reorganizes to `if cond: x = y`, but the second is not as clear, with a likely equivalent of `if cond: x[y] else raise Exception`. Thanks to Tom Forbes and Jim Kelly for helping critique the idea thus far. -- Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. <https://twitter.com/arroyo_networks> <https://www.linkedin.com/company/arroyo-networks> <https://www.github.com/ArroyoNetworks>
Hi Joshua Sounds to me that you want a solution soon, rather than in a future version of Python. Perhaps this works for you. def prune_nones(d): for k, v in list(d.items()): if v is None: del d[k] if type(v) is dict: prune_nones(v)
d = dict(a=1, b=2, c=None) prune_nones(d) {'a': 1, 'b': 2}
d = dict(a=1, b=2, c=None, d=dict(e=None, f=3)) prune_nones(d) {'a': 1, 'b': 2, 'd': {'f': 3}}
I hope this helps. By the way, the list(d.items()) in the loop is to avoid RuntimeError: dictionary changed size during iteration -- Jonathan
On Fri, Apr 26, 2019 at 11:07 AM Joshua Marshall <j.marshall@arroyo.io> wrote:
Hello all,
I have a use case where I need to send a `dict` to a module as an argument. Inside of this, it has a multi-level structure, but each field I need to set may only be set to a single value. Fields must be valid, non-empty strings. It looks a lot like the following in my code:
``` def my_func(val_1, val_2): return { "field_1": val_1, "next_depth": { "field_2": val_2 } } ```
What I want to do is: ``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ```
If val_2 here evaluates to falsey, will next_depth still be defined? From the code I would say that no. But your use case may require to not define the next_depth subdict without any values, as that may break the receiver expectations (think of JSON Schema).
Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } } ```
Where each conditional in this context functions as: ``` if value: d["your_key"] = value ``` for each conditionally added and set key.
From the slack channel #learning_python, there are a number of more general points which need to be handled. The more core syntax, which should be valid throughout the language, would be to have statements like `x = y if cond` and `x[y if cond]`. The first of these intuitively reorganizes to `if cond: x = y`, but the second is not as clear, with a likely equivalent of `if cond: x[y] else raise Exception`.
Thanks to Tom Forbes and Jim Kelly for helping critique the idea thus far.
Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment.
<https://twitter.com/arroyo_networks> <https://www.linkedin.com/company/arroyo-networks> <https://www.github.com/ArroyoNetworks> _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Sebastian Kreft
Ideally, the "next_depth" field would also not be defined, which may be easier to handle with the later syntax of putting an 'if' out in front. On Fri, Apr 26, 2019 at 11:56 AM Sebastian Kreft <skreft@gmail.com> wrote:
On Fri, Apr 26, 2019 at 11:07 AM Joshua Marshall <j.marshall@arroyo.io> wrote:
Hello all,
I have a use case where I need to send a `dict` to a module as an argument. Inside of this, it has a multi-level structure, but each field I need to set may only be set to a single value. Fields must be valid, non-empty strings. It looks a lot like the following in my code:
``` def my_func(val_1, val_2): return { "field_1": val_1, "next_depth": { "field_2": val_2 } } ```
What I want to do is: ``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ```
If val_2 here evaluates to falsey, will next_depth still be defined? From the code I would say that no. But your use case may require to not define the next_depth subdict without any values, as that may break the receiver expectations (think of JSON Schema).
Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } } ```
Where each conditional in this context functions as: ``` if value: d["your_key"] = value ``` for each conditionally added and set key.
From the slack channel #learning_python, there are a number of more general points which need to be handled. The more core syntax, which should be valid throughout the language, would be to have statements like `x = y if cond` and `x[y if cond]`. The first of these intuitively reorganizes to `if cond: x = y`, but the second is not as clear, with a likely equivalent of `if cond: x[y] else raise Exception`.
Thanks to Tom Forbes and Jim Kelly for helping critique the idea thus far.
Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment.
<https://twitter.com/arroyo_networks> <https://www.linkedin.com/company/arroyo-networks> <https://www.github.com/ArroyoNetworks> _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Sebastian Kreft
-- Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. <https://twitter.com/arroyo_networks> <https://www.linkedin.com/company/arroyo-networks> <https://www.github.com/ArroyoNetworks>
On 2019-04-26 16:56, Sebastian Kreft wrote:
On Fri, Apr 26, 2019 at 11:07 AM Joshua Marshall <j.marshall@arroyo.io <mailto:j.marshall@arroyo.io>> wrote:
Hello all,
I have a use case where I need to send a `dict` to a module as an argument. Inside of this, it has a multi-level structure, but each field I need to set may only be set to a single value. Fields must be valid, non-empty strings. It looks a lot like the following in my code:
``` def my_func(val_1, val_2): return { "field_1": val_1, "next_depth": { "field_2": val_2 } } ```
What I want to do is: ``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ```
If val_2 here evaluates to falsey, will next_depth still be defined? From the code I would say that no. But your use case may require to not define the next_depth subdict without any values, as that may break the receiver expectations (think of JSON Schema).
From the code I would say yes. If you didn't want the subdict, you would've written: def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 } if val_2 }
Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } } ```
def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, if val_2: "next_depth": { "field_2": val_2 } } [snip] The first form is too easily confused with the ternary 'if'. In Python, an expression never starts with an 'if', so the second form would be a better syntax for an optional entry.
On 4/26/19 11:03 AM, Joshua Marshall wrote:
Hello all,
I have a use case where I need to send a `dict` to a module as an argument. Inside of this, it has a multi-level structure, but each field I need to set may only be set to a single value. Fields must be valid, non-empty strings. It looks a lot like the following in my code:
``` def my_func(val_1, val_2): return { "field_1": val_1, "next_depth": { "field_2": val_2 } } ```
What I want to do is: ``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ```
It's not clear in this example what you would want if val_2 is None. Should it be: { "field_1": val_1 } or: { "field_1": val_1, "next_depth": {} } ? Better would be to build your dict with the tools you already have: d = {} if val_1: d['field_1'] = val_1 if val_2: d['next_depth'] = { 'field_2': val_2 } You have total control over the results, and it doesn't take much more space than your proposal. Various helper function could make the code more compact, and even clearer than your proposal: d = {} add_maybe(d, val_1, "field_1") add_maybe(d, val_2, "next_depth", "field_2") Of course, you might prefer a different API. That's an advantage of helper functions: you can design them to suit your exact needs. --Ned.
Others have responded, but a note:
What I want to do is:
``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ``` I am finding this very confusing as to how to generalize this: How do we know that val_1 belongs to the "top-level" field_1, and val_2 is in the nested dict with field_2? Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } } but this makes it seem like that distinction is hard-coded -- so is the nested dict is relevant?
The more core syntax, which should be valid throughout the language, would be to have statements like `x = y if cond`
we have the x = y if cond else expression already -- and an assignment HAS to be assigned to something, so it seems what you want is: x = y if cond else None Maybe the "else None" feels like too much typing, but I prefer the explicitness myself. (and look in the history of this thread for "null coalescing" discussion, that _may_ be relevant. The first of these intuitively reorganizes to `if cond: x = y` then what do we get for x `if not cond`? it ends up undefined? or set to whatever value it used to have? Frankly, I think that's a mistake -- you're going to end up with having to trap a NameError or do a a hasattr() check later on anyway. It's generally considered good practice to set a name to None if it isn't defined, rather than not defining it.
and `x[y if cond]` ... But the second is not as clear, with a likely equivalent of `if cond: x[y] else raise Exception`.
assuming x is a dict, then you could do: d[y if cond else []] = value It's a hack, but as lists aren't hashable, you get an TypeError, so maybe that would work for you? example: In [16]: key = "Fred" In [17]: value = "Barnes" In [18]: d = {} In [19]: # If the key is Truthy: In [20]: d[key if key else []] = value In [21]: d Out[21]: {'Fred': 'Barnes'} In [22]: # if the key is Falsey: In [23]: key = None In [24]: d[key if key else []] = value --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-24-170a67b9505a> in <module>() ----> 1 d[key if key else []] = value TypeError: unhashable type: 'list' -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Calling upon ol' Guidos Time Machinne: In [31]: def create(val1): ...: data = { ...: **({"val1": "me here"} if val1 else {}) ...: } ...: return data ...: In [32]: create(False) Out[32]: {} In [33]: create(True) Out[33]: {'val1': 'me here'} Now, please, just move to the next request to the language. I think the in-place star exapanding, along with the inline if like above can make out for all of your use-cases. If it can't, then expect xpression assignment-s (the `a:=1` from PEP 572, comming in Python 3.8) to cover for the rest - as you can define variables inside the `if` expressions like the above which could be re-used in other `if`s (or even in key-names), further down the dictionary) So, seriously - On one hand, Python's syntaxalready allow what you are requesting. On the other hand, it makes use of a syntax that is already little used (in place star-expansion for mappings), to a point that in this thread, up to here, no one made use of it. Threfore, IMHO, it demonstrates that adding arbitrary syntaxes and language features ultimatelly fills the language with clutter very few people ends up knowing how to use - - we should now work on what is already possible instead of making the language still more difficult to learn . (respasting the code so you can further experiment): def create(val1): data = { **({"val1": "me here"} if val1 else {}) } return data On Fri, 26 Apr 2019 at 21:22, Christopher Barker <pythonchb@gmail.com> wrote:
Others have responded, but a note:
What I want to do is:
``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ```
I am finding this very confusing as to how to generalize this:
How do we know that val_1 belongs to the "top-level" field_1, and val_2 is in the nested dict with field_2?
Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } }
but this makes it seem like that distinction is hard-coded -- so is the nested dict is relevant?
The more core syntax, which should be valid throughout the language, would be to have statements like `x = y if cond`
we have the
x = y if cond else
expression already -- and an assignment HAS to be assigned to something, so it seems what you want is:
x = y if cond else None
Maybe the "else None" feels like too much typing, but I prefer the explicitness myself. (and look in the history of this thread for "null coalescing" discussion, that _may_ be relevant.
The first of these intuitively reorganizes to `if cond: x = y`
then what do we get for x `if not cond`? it ends up undefined? or set to whatever value it used to have?
Frankly, I think that's a mistake -- you're going to end up with having to trap a NameError or do a a hasattr() check later on anyway. It's generally considered good practice to set a name to None if it isn't defined, rather than not defining it.
and `x[y if cond]` ... But the second is not as clear, with a likely equivalent of `if cond: x[y] else raise Exception`.
assuming x is a dict, then you could do:
d[y if cond else []] = value
It's a hack, but as lists aren't hashable, you get an TypeError, so maybe that would work for you?
example:
In [16]: key = "Fred" In [17]: value = "Barnes" In [18]: d = {}
In [19]: # If the key is Truthy: In [20]: d[key if key else []] = value
In [21]: d Out[21]: {'Fred': 'Barnes'}
In [22]: # if the key is Falsey: In [23]: key = None
In [24]: d[key if key else []] = value --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-24-170a67b9505a> in <module>() ----> 1 d[key if key else []] = value
TypeError: unhashable type: 'list'
-CHB
-- Christopher Barker, PhD
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
yes, this can already be done, and yes, mapping unpacking is little used (I never think to use it). But in this case, it's a no-op, not sure the point. I get the same thing with just the ternary expression: In [11]: def create(val1): ...: data = {"val1": "me here"} if val1 else {} ...: return data ...: In [12]: create(True) Out[12]: {'val1': 'me here'} In [13]: create(False) Out[13]: {} -CHB On Sat, Apr 27, 2019 at 8:25 AM Joao S. O. Bueno <jsbueno@python.org.br> wrote:
Calling upon ol' Guidos Time Machinne:
In [31]: def create(val1): ...: data = { ...: **({"val1": "me here"} if val1 else {}) ...: } ...: return data ...:
In [32]: create(False)
Out[32]: {}
In [33]: create(True)
Out[33]: {'val1': 'me here'}
Now, please, just move to the next request to the language.
I think the in-place star exapanding, along with the inline if like above can make out for all of your use-cases. If it can't, then expect xpression assignment-s (the `a:=1` from PEP 572, comming in Python 3.8) to cover for the rest - as you can define variables inside the `if` expressions like the above which could be re-used in other `if`s (or even in key-names), further down the dictionary)
So, seriously - On one hand, Python's syntaxalready allow what you are requesting. On the other hand, it makes use of a syntax that is already little used (in place star-expansion for mappings), to a point that in this thread, up to here, no one made use of it. Threfore, IMHO, it demonstrates that adding arbitrary syntaxes and language features ultimatelly fills the language with clutter very few people ends up knowing how to use - - we should now work on what is already possible instead of making the language still more difficult to learn .
(respasting the code so you can further experiment):
def create(val1): data = { **({"val1": "me here"} if val1 else {}) } return data
On Fri, 26 Apr 2019 at 21:22, Christopher Barker <pythonchb@gmail.com> wrote:
Others have responded, but a note:
What I want to do is:
``` def my_func(val_1, val_2): return { "field_1": val_1 if val_1, "next_depth": { "field_2": val_2 if val_2 } } ```
I am finding this very confusing as to how to generalize this:
How do we know that val_1 belongs to the "top-level" field_1, and val_2 is in the nested dict with field_2?
Or: ``` def my_func(val_1, val_2): return { if val_1 : "field_1": val_1, "next_depth": { if val_2: "field_2": val_2 } }
but this makes it seem like that distinction is hard-coded -- so is the nested dict is relevant?
The more core syntax, which should be valid throughout the language, would be to have statements like `x = y if cond`
we have the
x = y if cond else
expression already -- and an assignment HAS to be assigned to something, so it seems what you want is:
x = y if cond else None
Maybe the "else None" feels like too much typing, but I prefer the explicitness myself. (and look in the history of this thread for "null coalescing" discussion, that _may_ be relevant.
The first of these intuitively reorganizes to `if cond: x = y`
then what do we get for x `if not cond`? it ends up undefined? or set to whatever value it used to have?
Frankly, I think that's a mistake -- you're going to end up with having to trap a NameError or do a a hasattr() check later on anyway. It's generally considered good practice to set a name to None if it isn't defined, rather than not defining it.
and `x[y if cond]` ... But the second is not as clear, with a likely equivalent of `if cond: x[y] else raise Exception`.
assuming x is a dict, then you could do:
d[y if cond else []] = value
It's a hack, but as lists aren't hashable, you get an TypeError, so maybe that would work for you?
example:
In [16]: key = "Fred" In [17]: value = "Barnes" In [18]: d = {}
In [19]: # If the key is Truthy: In [20]: d[key if key else []] = value
In [21]: d Out[21]: {'Fred': 'Barnes'}
In [22]: # if the key is Falsey: In [23]: key = None
In [24]: d[key if key else []] = value
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-24-170a67b9505a> in <module>() ----> 1 d[key if key else []] = value
TypeError: unhashable type: 'list'
-CHB
-- Christopher Barker, PhD
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
participants (7)
-
Christopher Barker
-
Joao S. O. Bueno
-
Jonathan Fine
-
Joshua Marshall
-
MRAB
-
Ned Batchelder
-
Sebastian Kreft