Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most Pythonic way to provide a fall-back value in an assignment?

Tags:

python

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.

like image 224
maxaposteriori Avatar asked Apr 20 '09 13:04

maxaposteriori


5 Answers

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)
like image 181
vartec Avatar answered Sep 27 '22 17:09

vartec


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
like image 31
SilentGhost Avatar answered Sep 27 '22 18:09

SilentGhost


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;
like image 20
Hinrik Avatar answered Sep 27 '22 17:09

Hinrik


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)
like image 44
SingleNegationElimination Avatar answered Sep 27 '22 16:09

SingleNegationElimination


There's python's ternary operation:

a = x if x is not None else y

Available in 2.5 and up.

like image 43
Dana Avatar answered Sep 27 '22 16:09

Dana