tags:

views:

464

answers:

9

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.

+20  A: 

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)
vartec
Way better than mine...
Greg
hm, so x is defined after all :/
SilentGhost
@greg: it's not better than yours but it's equally mistaken
SilentGhost
@SilentGhost: for the problem I was solving (x is defined and None), I think this is easier to read.
Greg
This soultion doesn't work when x is 0 or negative or an empty list etc.
Randle Taylor
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
+1  A: 

There's python's ternary operation:

a = x if x is not None else y

Available in 2.5 and up.

Dana
A: 

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
I have no idea what coerced you to add the if else in MyFunc. If you kill the if, else, and else block, it all still works the same.
Stefan Kendall
Of course it does - I was just trying to illustrate the results!
Jon Cage
+1 to neutralize since you corrected. :)
Shane C. Mason
+1  A: 

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?

rpr
+5  A: 

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
Sorry, yes, this wasn't actually what I was looking for but is a good answer to the question I actually asked!
Andy
@Andy: read most upvoted comment to the accepted answer to see why it's not answering your question actually
SilentGhost
This solution seems like the proper way to do it. You could make it more concise by just doing: try: a = x except: a = y
Randle Taylor
You should make sure to catch an explicit NameError.
miracle2k
A: 

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
Randle Taylor
+3  A: 

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)
TokenMacGuy
+1 Agreed 100% when you say "because this is not a pattern that is pythonic"
Shane C. Mason
+3  A: 

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
A: 

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