我们可以在条件中安排作业吗?


92

是否有条件分配作业?

对于前。

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

两个问题具有相同的答案并不意味着它们是重复的。回答自己很琐碎,但看不出推理的理由,或者是否有解决方法。
mehmet

Answers:


111

为什么不尝试一下呢?

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

所以不行。

更新: 在Python 3.8中可以(使用不同的语法)


35
这是有意禁止的,因为仁慈的Python独裁者Guido认为它们是不必要的,而且比有用的更令人困惑。这是没有后递增或前递增运算符(++)的相同原因。
马特·博姆

4
他没有允许添加的增强分配新建分配FY在2.0,因为x = x + 1需要额外的查找时间,而x += 1有点快,但我敢肯定,他甚至不喜欢做那个了。:-)
wescpy

希望,这将很快成为可能。Python发展缓慢
Nik O'Lai

4
“为什么不尝试一下?”-因为谁知道语法可能是什么?也许OP尝试过但没有奏效,但这并不意味着语法没有不同,或者没有没有
Levi H

57

更新-原始答案接近底部

Python 3.8将引入PEP572

摘要
这是创建一种使用NAME:= expr表示法分配表达式中变量的方法的建议。添加了一个新异常TargetScopeError,并且评估顺序有所更改。

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

“ PEP 572混乱”是由仁慈的生命独裁者Guido van Rossum领导的2018年Python语言峰会会议的主题。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都绑定在包含范围内(即与结果或填充相同的级别)。

捕获条件值可以在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循环,这可以消除对无限循环,赋值和条件的需求。它还在仅使用函数调用作为条件的循环与将其用作条件但也使用实际值的循环之间创建了平滑的并行处理。

分支来自底层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-ti-use-an-assignment-in-an-expression.htm


我喜欢这个答案,因为它实际上指出了为什么有意将这样的“功能”遗漏在Python中。在教初学者编程时,我已经看到很多人if (foo = 'bar')在测试的值时犯了这个错误foo
乔纳森·克罗斯

2
@JonathanCross,实际上将在3.8中添加此“功能”。这是不太可能被用作谨慎,因为它应该-但至少它不是一个简单的=
约翰·拉ROOY

@JohnLaRooy:看这些例子,我认为“不太可能尽量少用”;在大约10个示例中,我发现只有两个实际上可以改进代码。(即,在一段时间内作为唯一的表达方式,以避免重复行或在体内出现循环条件,或者在
省略号

39

不,BDFL不喜欢该功能。

在我的位置上,“终身慈善独裁者”的Guido van Rossum一直在努力使Python尽可能简单。但是,实际上并没有一个设计Python的委员会,而是一个可信赖的“顾问委员会”,它实际上是基于优点,通过一个设计师的敏感性进行过滤,这一事实产生了一种很好的语言,恕我直言。


15
简单?此功能可以简化我的一些代码,因为它可以使其更紧凑并因此更具可读性。现在,我需要两行,而以前只需要一行。我从来没有说过为什么Python多年来拒绝其他编程语言具有的功能(而且通常是出于非常充分的理由)。特别是我们在这里谈论的这个功能非常非常有用。
里吉斯

6
更少的代码并不总是更简单或更易于阅读。以递归函数为例。它的循环等效性通常更具可读性。
FMF

1
我不喜欢它的C版本,但是if let当我拥有if elif链时,我真的很想念有rust的东西,但是在每种情况下都需要存储和使用条件的值。
Thayne

1
我必须说我现在正在编写的代码(搜索此问题的原因)非常难看,没有此功能。我需要在最后一个else下继续缩进下一个if,而不是使用if后跟许多其他if。
MikeKulls

17

按照我的这个老食谱,不是直接-但正如食谱所说的,构建语义等效项很容易,例如,如果您需要直接从C编码的参考算法音译(当然,在重构为更惯用的Python之前; -)。即:

class DataHolder(object):
    def __init__(self, value=None): self.value = value
    def set(self, value): self.value = value; return value
    def get(self): return self.value

data = DataHolder()

while data.set(somefunc()):
  a = data.get()
  # use a

顺便说一句,对于您的特定情况,这是一种非常惯用的Python形式,如果您确切知道somefunc在返回虚假值(例如0)时可能返回的虚假值是

for a in iter(somefunc, 0):
  # use a

因此在这种特定情况下,重构将非常容易;-)。

如果返回可能是任何一种falsish值(0, None'',...),一种可能性是:

import itertools

for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
    # use a

但您可能更喜欢简单的自定义生成器:

def getwhile(func, *a, **k):
    while True:
      x = func(*a, **k)
      if not x: break
      yield x

for a in getwhile(somefunc):
    # use a

如果可以的话,我将投票两次。对于那些确实需要这种情况的时代,这是一个很好的解决方案。我将您的解决方案调整为正则表达式Matcher类,该类实例化一次,然后在if语句中使用.check(),在其主体内部使用.result()来检索匹配项(如果存在)。谢谢!:)
Teekin '18

16

是的,但仅适用于Python 3.8及更高版本。

PEP 572建议分配表达式,并且已经被接受。

引用PEP的“语法和语义”部分:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match

# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
   process(chunk)

# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]

在您的特定情况下,您将能够写

if a := some_func():
    # Use a

5

否。Python中的赋值是语句,而不是表达式。


Guido不会有其他方式。
Mark Ransom

1
@MarkRansom大家都欢呼吉多。..叹气。
StephenBoesch

@javadba这个家伙正确的次数比错误的错误要多得多。我赞赏有一个负责愿景的人所产生的战略要比委员会设计的战略更为连贯;我可以将C ++作为我的主要面包和黄油进行比较和对比。
Mark Ransom

我觉得ruby和scala(使用不同的语言)都比python正确得多:但是无论如何这都不是地方
。– StephenBoesch

4

由于Python的3.8新功能将有可能做到从这个版本这样的事情,虽然没有使用=,但阿达般的赋值运算符:=。来自文档的示例:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match

2

您可以定义一个函数来为您分配:

def assign(name, value):
    import inspect
    frame = inspect.currentframe()
    try:
        locals_ = frame.f_back.f_locals
    finally:
        del frame 
    locals_[name] = value
    return value

if assign('test', 0):
    print("first", test)
elif assign('xyz', 123):
    print("second", xyz)

1

在某些情况下分配是非法的原因之一是更容易犯错误并分配True或False:

some_variable = 5

# This does not work
# if True = some_variable:
#   do_something()

# This only works in Python 2.x
True = some_variable

print True  # returns 5

在Python 3中,True和False是关键字,因此不再存在风险。


1
在[161]:l_empty == []出[161]:真[162]:[] == []出[162]:真我不认为这是原因
火山

可以肯定的是,大多数人还是放在== True右边。
numbermaniac

1

赋值运算符-也非正式地称为海象运算符-于2018年2月28日在PEP572创建

为了完整起见,我将发布相关部分,以便您可以比较3.7和3.8之间的差异:

3.7
---
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
test: or_test ['if' or_test 'else' test] | lambdef
test_nocond: or_test | lambdef_nocond
lambdef: 'lambda' [varargslist] ':' test
lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*

3.8
---
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
namedexpr_test: test [':=' test]                         <---- WALRUS OPERATOR!!!
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.