34

In Perl, it's often nice to be able to assign an object, but specify some fall-back value if the variable being assigned from is 'undef'. For instance:

my $x = undef;
my $y = 2;
my $a = $x || $y;

After this,

$a == 2

Is there a concise way to achieve this in Python if the value x is None, or would a full-on ...

if x is not None
    a = x
else
    a = y

... be the most Pythonic way to achieve this?

EDIT: Apologies, as has been pointed out by several commenters, I wasn't really talking about the value being undefined, but 'undef' in Perl, which is not really the same thing. But the question as originally worded didn't make this clear.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
maxaposteriori
  • 7,267
  • 4
  • 28
  • 25
  • 3
    Please keep in mind that "if the variable being assigned from is undefined" is WRONG. "if the variable being assigned from is false" is much closer to the truth. – innaM Apr 20 '09 at 13:19
  • 1
    To follow up Manni's comment: it's better to use the defined-or operator "//" if you can rely on Perl 5.10 or later. (Not that this has any bearing on how you would do it in Python.) – Michael Carman Apr 20 '09 at 14:42

9 Answers9

59

Since 2.5:

If you want to fall back only on None:

a = x if x is not None else y 

If you want to fall back also on empty string, false, 0 etc.:

a = x if x else y 

or

a = x or y 

As for undefined (as never defined, a.k.a. not bound):

try:
  a = x 
except NameError:
  a = y

or a bit more hackish (I'd not really recommend that, but it's short):

a = vars().get('x',y)
sykora
  • 96,888
  • 11
  • 64
  • 71
vartec
  • 131,205
  • 36
  • 218
  • 244
  • hm, so x is defined after all :/ – SilentGhost Apr 20 '09 at 13:18
  • @greg: it's not better than yours but it's equally mistaken – SilentGhost Apr 20 '09 at 13:37
  • @SilentGhost: for the problem I was solving (x is defined and None), I think this is easier to read. – Greg Apr 20 '09 at 14:05
  • This soultion doesn't work when x is 0 or negative or an empty list etc. – randlet Apr 20 '09 at 14:51
  • There are two solutions here, and the conditions under which they work are clearly noted (esp. given the edit that perl's "undef" is not the same as python's NameError... ie: undefined). It's a good answer as it offers a quick-fix, along with explanation for areas where the quick fix doesn't apply. – Jarret Hardie Apr 22 '09 at 00:33
6

first you can do your full-on with a ternary:

a = y if x is None else x

but it doesn't solve your problem. what you want to do is more closely implemented with:

try:
    a = x
except:
    a = y
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
4

I am quite convinced that there is no 'pythonic' way to do this, because this is not a pattern that is pythonic. Control should not reach an undefined variable reference in elegant code. There are similar ideas that are pythonic. Most obvious:

def myRange(start, stop=None):
    start, stop = (0, start) if stop is None else (start, stop)
    ...

What's important is that stop is defined in scope, but the caller didn't have to pass it explicitly, only that it has taken it's default value, altering the semantics of the arguments, which in effect causes the first argument to be optional instead of the second, even where the language does not allow that without this clever trick.

That being said, something like this might follow the premise without using a try-catch block.

a = locals().get('x', y)
brian d foy
  • 129,424
  • 31
  • 207
  • 592
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
4

Just some nitpicking with your Perl example:

my $x = undef;

This redundant code can be shortened to:

my $x;

And the following code doesn't do what you say it does:

my $a = $x || $y;

This actually assigns $y to $a when $x is false. False values include things like undef, zero, and the empty string. To only test for definedness, you could do the following (as of Perl 5.10):

my $a = $x // $y;
Hinrik
  • 819
  • 9
  • 14
1

There's python's ternary operation:

a = x if x is not None else y

Available in 2.5 and up.

Dana
  • 32,083
  • 17
  • 62
  • 73
1

I think this would help, since the problem comes down to check whether a variable is defined or not:
Easy way to check that variable is defined in python?

Community
  • 1
  • 1
rpr
  • 3,768
  • 2
  • 22
  • 20
0

Most of the solutions relying on if statements don't work for the case where x is 0 or negative.

>>> x = 0
>>> y = 2
>>> a = x or y
>>> a
2
>>> 

If you knew the name of the variable ahead of time you could look for like so:

if 'x' in dir():
    a = x 
except:
     a =y

However that solution seems kind of sloppy to me. I believe the best method is to use a try except block like so:

try:
    a = x
else:
    a = y
randlet
  • 3,628
  • 1
  • 17
  • 21
0

One way to rewrite...

if x is not None
    a = x
else
    a = y

..is:

x = myfunction()

if x is None:
    x = y

print x

Or, using exceptions (possibly more Python'y, depending on the what the code is doing - if it returns None because there was an error, using an exception is probably the correct way):

try:
    x = myfunction()
except AnException:
    x = "fallback"

print x

All that said, there really isn't anything wrong with you original code:

if x is not None
    a = x
else
    a = y

It's long, but I find that far easier to read (and much more Pythonic) than either of the following one-liners:

a = x if x is not None else y
a = x or y
dbr
  • 165,801
  • 69
  • 278
  • 343
-1

If it's an argument to a function you can do this:

def MyFunc( a=2 ):
    print "a is %d"%a

>>> MyFunc()
...a is 2
>>> MyFunc(5)
...a is 5

[Edit] For the downvoters.. the if/else bit is unnecessary for the solution - just added to make the results clear. Edited it to remove the if statement if that makes it clearer?

Jon Cage
  • 36,366
  • 38
  • 137
  • 215