22

Is there a way to do an assignment only if the assigned value is not None, and otherwise do nothing?

Of course we can do:

x = get_value() if get_value() is not None

but this will read the value twice. We can cache it to a local variable:

v = get_value()
x = v if v is not None

but now we have made two statements for a simple thing.

We could write a function:

def return_if_not_none(v, default):
    if v is not None:
        return v
    else:
        return default

And then do x = return_if_not_none(get_value(), x). But surely there is already a Python idiom to accomplish this, without accessing x or get_value() twice and without creating variables?

Put in another way, let's say =?? is a Python operator similar to the C# null coalesce operator. Unlike the C# ??=, our fictional operator checks if the right hand side is None:

x = 1
y = 2
z = None

x =?? y
print(x)   # Prints "2"

x =?? z
print(x)   # Still prints "2"

Such a =?? operator would do exactly what my question is asking.

Donentolon
  • 1,363
  • 1
  • 10
  • 17
  • `value()` is a function, not a value. Is it right? – Sangbok Lee Jan 17 '20 at 01:39
  • 10
    What about `x = value() or x` It is just as verbose as `x = value() if value()` but only executes value() once. https://stackoverflow.com/a/48813117/2193968 https://stackoverflow.com/a/4978745/2193968 – Jerry Jeremiah Jan 17 '20 at 01:39
  • @SangbokLee I don't think so. Look at the links I provided. – Jerry Jeremiah Jan 17 '20 at 01:43
  • I rewrote slightly to make it clear. – Donentolon Jan 17 '20 at 01:45
  • @JerryJeremiah yes, but it accesses x twice – Donentolon Jan 17 '20 at 01:45
  • 2
    This question is under specified. Do you mean *assign if x is falsy*, do you mean *assign if x is `None`*, do you mean *assign if x has not previously been assigned a value*, or is it something else? Python doesn't have a null. – Ryan Haining Jan 17 '20 at 01:47
  • 1
    @RyanHaining I mean: Assign if `get_value()` is not `None`, do nothing otherwise. – Donentolon Jan 17 '20 at 01:48
  • 1
    @Donentolon Does `x = value() or x` really access value() twice? It doesn't look like it but python could do anything internally... – Jerry Jeremiah Jan 17 '20 at 01:49
  • @JerryJeremiah It can presumably access `x` twice if `value()` is None: Once to read, once to write. Perhaps there is some optimization, but the point is that `x` is written twice in the source code. – Donentolon Jan 17 '20 at 01:50
  • 1
    @JerryJeremiah doesn't really matter, because OP only cares about `None`, so if `value()` returns empty string, 0, or any other false value, it won't work. – Ryan Haining Jan 17 '20 at 01:52
  • @Donentolon Sorry I didn't realize that your question wasn't about actualy running the get_value() function twice but rather about having neither x no get_value() visible in the source code more than once. That's not clear in your question at all. When I read the question I understood it to ask how to avoid running the get_value() function more than once with code on a single line. – Jerry Jeremiah Jan 17 '20 at 01:53
  • 1
    `x = value() or x` will assign `x = x` if `value()` is `None`. But OP wants 'do nothing' if `value()` is `None`. We need whatsoever `if` here. – Sangbok Lee Jan 17 '20 at 01:53
  • @RyanHaining The question was edited after I made all those comments. I guess I could delete them... – Jerry Jeremiah Jan 17 '20 at 01:54
  • @SangbokLee And the answer already posted doesn't do that either but it has upvotes.... – Jerry Jeremiah Jan 17 '20 at 01:55
  • 2
    `name = expr if expr is not None` is not valid. Do you mean `if expr is not None: name = expr`? – wjandrea Jan 17 '20 at 02:22

2 Answers2

20

In python 3.8 you can do something like this

if (v := get_value()) is not None:
    x = v

Updated based on Ryan Haining solution, see in comments

wjandrea
  • 28,235
  • 9
  • 60
  • 81
alex2007v
  • 1,230
  • 8
  • 12
  • Does that really assign 1 to x if value() returns null? Shouldn't it assign x to x so that the value remains unchanged? – Jerry Jeremiah Jan 17 '20 at 01:45
  • I assumed that should be some value that we will assign in case when `value()` returns `None`. But if we have to restore x value this will not work – alex2007v Jan 17 '20 at 01:48
  • 4
    `if (v := get_value()) is not None): x = v` – Ryan Haining Jan 17 '20 at 01:51
  • 1
    It will introduce additional variable, I thought question was about avoiding this. – alex2007v Jan 17 '20 at 01:52
  • OP has mixed requirements, another seems to be that they don't want to rewrite `x`. Your proposal works easily when the value of `x` is known when writing the code, but if it were dynamic, you'd have to store `x`'s old value before possibly overwriting with `value()`. Further, you want `is not None` because OP also only cares about ignoring `None`, not all falsy value. – Ryan Haining Jan 17 '20 at 01:55
  • Still doesn't answer the question the OP actually wanted to ask (including the part about not mentioning any variable more than once and not creating new ones) but it is a GOOD answer and has my upvote. – Jerry Jeremiah Jan 17 '20 at 02:03
  • Python 3.8 one liner: x = v if (v:=get_value()) is not None else x – erny Apr 06 '21 at 10:10
  • Although, this may give some error in linters "undefined-variable", so we can invert the condition to `x = x if (v:=get_value()) is None else v` – erny Apr 06 '21 at 10:21
0

You can also just use the or operator if you want something neater.

In your example:

x = 1
y = 2
z = None

# x =?? y
x = y or x
print(x)   # Prints "2"

# x =?? z
x = z or x
print(x)   # Still prints "2"

Of course, order of variables matters here, and you have to be careful, since

x = x or y

Is meaningless if x is not None, and might even throw an exception if x was never defined before.

Elad Avron
  • 1,411
  • 1
  • 18
  • 28