在赋值中提供一个备用值的最Pythonic方式是什么?

34

在Perl中,通常希望能够分配一个对象,但如果要从分配的变量是'undef',则指定一些后备值会更好。例如:

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

在此之后,

$a == 2

如果值x为None,在Python中是否有简洁的方法来实现此目的,或者需要完整的...

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

这是实现此目的最具Python风格的方式吗?

编辑:抱歉,正如几位评论者所指出的那样,我实际上并不是在谈论值未定义,而是在谈论Perl中的 'undef',这并不完全是同一回事。 但最初措辞的问题并没有表达清楚。


3
请记住,“如果被赋值的变量未定义”是错误的。 “如果被赋值的变量为false”更接近事实。 - innaM
1
跟进Manni的评论:最好使用定义运算符“//”,如果您可以依赖于Perl 5.10或更高版本。(这并不影响您在Python中如何完成它。) - Michael Carman
9个回答

59

从2.5版本开始:

如果您仅想回退到 None

a = x if x is not None else y 

如果你希望也对空字符串、false0等进行回退:


a = x if x else y 
或者
a = x or y 

对于未定义的变量(即从未定义过、也就是未被绑定的变量):

try:
  a = x 
except NameError:
  a = y

或者更加短小粗暴一些的方法(我并不真正推荐这样做):

a = vars().get('x',y)

哦,原来 x 已经被定义了 :/ - SilentGhost
@greg:它不比你的更好,但同样是错误的。 - SilentGhost
@SilentGhost:对于我解决的问题(x已定义且为None),我认为这种方式更容易阅读。 - Greg
这个解决方案在x为0或负数或空列表等情况下无法工作。 - randlet
这里有两个解决方案,并且它们适用的条件已经清楚地注明(特别是考虑到perl的“undef”与python的NameError不同,即未定义)。这是一个很好的答案,因为它提供了快速修复,以及对快速修复不适用的区域的解释。 - Jarret Hardie

6

首先,你可以使用三元运算符来完成全面的操作:

a = y if x is None else x

但这并不能解决你的问题。你想做的更接近实现的方式是:

try:
    a = x
except:
    a = y

抱歉,是的,这实际上不是我在寻找的内容,但对于我实际提出的问题是一个很好的答案! - maxaposteriori
@Andy:阅读被投票最多的评论以查看为什么它实际上没有回答你的问题。 - SilentGhost
这个方案似乎是正确的做法。你可以通过只使用以下代码更加简洁: try: a = x except: a = y - randlet
你应该确保捕获明确的NameError。 - miracle2k

4

我非常确定在这个问题上没有“pythonic”(Python特有的优秀风格)的解决方法,因为这不是Pythonic的模式。在优雅的代码中,控制流不应到达未定义的变量引用。有类似的Pythonic的想法,其中最明显的是:

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

重要的是,stop在作用域中被定义,但调用者不需要显式地传递它,只需使用其默认值,改变参数的语义,从而使第一个参数成为可选项而不是第二个参数,即使在语言不允许这样做的情况下也能实现这种聪明的技巧。
话虽如此,像这样的东西可能会遵循前提条件,而不使用try-catch块。
a = locals().get('x', y)

+1 当你说“因为这不是 Pythonic 模式”时,我完全同意。 - Shane C. Mason

4

关于您的Perl示例,有一些小问题:

my $x = undef;

这段冗余的代码可以简化为:

my $x;

以下代码并不像你所说的那样工作:

my $a = $x || $y;

当 $x 为 false 时,这实际上将 $y 赋值给 $a。假值包括未定义的值、零和空字符串。如果只想测试是否已定义,可以按照以下方式操作(适用于 Perl 5.10):

my $a = $x // $y;

1

Python 中有三元操作符:

a = x if x is not None else y

适用于2.5及以上版本。


1

0

大多数依赖于if语句的解决方案对于x为0或负数的情况不起作用。

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

如果您提前知道变量名称,可以像这样进行查找:

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

然而,那种解决方案对我来说似乎有些粗糙。我认为最好的方法是使用try except块,像这样:

try:
    a = x
else:
    a = y

0

重写代码的一种方法是...

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

..是:

x = myfunction()

if x is None:
    x = y

print x

或者,使用异常(可能更符合Python的风格,具体取决于代码的功能 - 如果返回None是因为出现了错误,则使用异常可能是正确的方法):

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

print x

话虽如此,但您的原始代码并没有任何问题:

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

虽然这段代码很长,但我发现它比下面的任何一行代码都更易读(而且更符合 Python 风格):

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

-1
如果它是一个函数的参数,你可以这样做:
def MyFunc( a=2 ):
    print "a is %d"%a

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

[编辑] 对于那些给我点踩的人...if/else部分对于解决方案来说是不必要的 - 只是为了让结果更清晰而添加的。我已经将其编辑以删除if语句,如果这样更清晰的话?


我不知道是什么促使你在MyFunc中添加if else。如果你删除if、else和else块,它仍然能够正常工作。 - Stefan Kendall
当然可以 - 我只是试图说明结果! - Jon Cage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接