条件中可以有赋值吗?

142 投票
10 回答
147284 浏览
提问于 2025-04-15 21:23

在条件语句中可以进行赋值吗?

比如说:

if (a=some_func()):
    # Use a

10 个回答

39

不,这个功能并没有得到BDFL的认可。

从我的角度来看,Guido van Rossum,也就是“终身仁慈独裁者”,一直在努力保持Python的简单性。我们可以对他做出的一些决定提出不同意见——我希望他能更频繁地说‘不’。但我认为,Python并不是由一个委员会来设计的,而是由一个基于能力的“顾问委员会”来进行筛选,主要依靠一个设计者的感觉,这样的方式创造出了一个非常不错的编程语言。

92

更新 - 原始回答在底部

Python 3.8 将引入 PEP572

摘要
这是一个提案,目的是创建一种在表达式中给变量赋值的方法,使用的语法是 NAME := expr。新增了一个异常 TargetScopeError,并且评估顺序有一个变化。

https://lwn.net/Articles/757713/

“PEP 572 的混乱”是2018年Python语言峰会的一个话题,由终身仁慈独裁者(BDFL)Guido van Rossum 主持。PEP 572 试图将赋值表达式(或称“内联赋值”)添加到语言中,但在python-dev邮件列表上经历了长时间的讨论,甚至在python-ideas上也进行了多轮讨论。这些讨论常常充满争议,信息量巨大,以至于很多人可能都选择了忽略。在峰会上,Van Rossum 概述了这个特性提案,他似乎倾向于接受,但他也想讨论如何避免未来出现这种讨论爆炸的情况。

https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library

来自Python标准库的示例

site.py env_base 只在这些行中使用,将其赋值放在if语句中可以将其作为块的“头部”。

当前:

env_base = os.environ.get("PYTHONUSERBASE", None)
if env_base:
    return env_base

改进:

if env_base := os.environ.get("PYTHONUSERBASE", None):
    return env_base
_pydecimal.py

避免嵌套的if语句,并减少一个缩进级别。

当前:

if self._is_special:
    ans = self._check_nans(context=context)
    if ans:
        return ans

改进:

if self._is_special and (ans := self._check_nans(context=context)):
    return ans

copy.py 代码看起来更整齐,避免了多个嵌套的if语句。(关于这个例子的来源,请参见附录A。)

当前:

reductor = dispatch_table.get(cls)
if reductor:
    rv = reductor(x)
else:
    reductor = getattr(x, "__reduce_ex__", None)
    if reductor:
        rv = reductor(4)
    else:
        reductor = getattr(x, "__reduce__", None)
        if reductor:
            rv = reductor()
        else:
            raise Error(
                "un(deep)copyable object of type %s" % cls)

改进:

if reductor := dispatch_table.get(cls):
    rv = reductor(x)
elif reductor := getattr(x, "__reduce_ex__", None):
    rv = reductor(4)
elif reductor := getattr(x, "__reduce__", None):
    rv = reductor()
else:
    raise Error("un(deep)copyable object of type %s" % cls)
datetime.py

tz 只用于 s += tz,将其赋值放在if语句内部有助于显示其作用范围。

当前:

s = _format_time(self._hour, self._minute,
                 self._second, self._microsecond,
                 timespec)
tz = self._tzstr()
if tz:
    s += tz
return s

改进:

s = _format_time(self._hour, self._minute,
                 self._second, self._microsecond,
                 timespec)
if tz := self._tzstr():
    s += tz
return s

sysconfig.py 在while条件中调用 fp.readline(),在if语句中调用 .match() 使代码更紧凑,而不

使其更难理解。

当前:

while True:
    line = fp.readline()
    if not line:
        break
    m = define_rx.match(line)
    if m:
        n, v = m.group(1, 2)
        try:
            v = int(v)
        except ValueError:
            pass
        vars[n] = v
    else:
        m = undef_rx.match(line)
        if m:
            vars[m.group(1)] = 0

改进:

while line := fp.readline():
    if m := define_rx.match(line):
        n, v = m.group(1, 2)
        try:
            v = int(v)
        except ValueError:
            pass
        vars[n] = v
    elif m := undef_rx.match(line):
        vars[m.group(1)] = 0

简化列表推导式 通过捕获条件,列表推导式可以高效地映射和过滤:

results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]

同样,子表达式可以在主表达式中重复使用,通过第一次使用时给它命名:

stuff = [[y := f(x), x/y] for x in range(5)]

注意,在这两种情况下,变量 y 都绑定在包含的作用域中(即与 results 或 stuff 在同一层级)。

捕获条件值 赋值表达式可以在if或while语句的头部有效使用:

# Loop-and-a-half
while (command := input("> ")) != "quit":
    print("You entered:", command)

# Capturing regular expression match objects
# See, for instance, Lib/pydoc.py, which uses a multiline spelling
# of this effect
if match := re.search(pat, text):
    print("Found:", match.group(0))
# The same syntax chains nicely into 'elif' statements, unlike the
# equivalent using assignment statements.
elif match := re.search(otherpat, text):
    print("Alternate found:", match.group(0))
elif match := re.search(third, text):
    print("Fallback found:", match.group(0))

# Reading socket data until an empty string is returned
while data := sock.recv(8192):
    print("Received data:", data)

特别是在while循环中,这可以消除无限循环、赋值和条件的需要。它还在一个循环中创建了一个平滑的平行关系,该循环简单地使用函数调用作为条件,而另一个则使用该条件并使用实际值。

Fork 来自低级UNIX世界的一个例子:

if pid := os.fork():
    # Parent code
else:
    # Child code

原始回答

http://docs.python.org/tutorial/datastructures.html

请注意,在Python中,与C不同,赋值不能出现在表达式中。C程序员可能会对此抱怨,但这避免了C程序中常见的一类问题:在表达式中输入=时,实际上是想输入==。

另见:

http://effbot.org/pyfaq/why-can-t-i-use-an-assignment-in-an-expression.htm

174

为什么不试试看呢?

>>> def some_func():
...   return 2
... 
>>> if (a = some_func()):
  File "<stdin>", line 1
    if (a = some_func()):
          ^
SyntaxError: invalid syntax

所以,不行。

更新: 在Python 3.8中,这种写法是可能的(语法不同)

if a := some_func():

撰写回答