条件付きで割り当ててもらえますか?


92

条件付きでの割り当ては可能ですか?

例:

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

2つの質問の答えが同じであるからといって、それらが重複しているわけではありません。自分自身に答えるのは簡単ですが、理由を確認したり、回避策があるかどうかを確認するのは簡単ではありません。
メフメト

回答:


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で可能です(異なる構文で)


36
慈悲深いPython独裁者であるGuidoは、それらが不必要であり、有用というよりも混乱していると考えているため、これは意図的に禁止されています。これは、ポストインクリメントまたはプレインクリメント演算子(++)がないのと同じ理由です。
Matt Boehm

4
彼は2.0で拡張割り当ての追加を許可しました。これx = x + 1は、ルックアップ時間x += 1がいくらか高速であるため、追加のルックアップ時間が必要なためですが、彼はそれほど多くのことをするのが好きではなかったと確信してます。:-)
wescpy 2010

願わくば、これはすぐに可能になるでしょう。Pythonの進化は遅い
NikO'Lai20年

5
「試してみませんか」-構文が何であるか誰が知っているのでしょうか?たぶんOPがいることをしようと、それはうまくいきませんでしたが、それは構文が異なっていない、または意図していない、それを行う方法がないということを意味するものではありません
レヴィH

59

更新-元の答えは下部にあります

Python3.8PEP572を導入します

要約
これは、NAME:= exprという表記を使用して式内の変数に割り当てる方法を作成するための提案です。新しい例外TargetScopeErrorが追加され、評価順序が1つ変更されました。

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

「PEP572混乱」は、慈悲深き独裁者(BDFL)のグイドヴァンロッサムが率いる2018年のPython言語サミットセッションのトピックでした。PEP 572は、言語に代入式(または「インライン代入」)を追加しようとしますが、python-ideasで複数回ラウンドした後でも、python-devメーリングリストの複数の巨大なスレッドについて長い議論がありました。それらのスレッドはしばしば論争を呼んでおり、多くの人がおそらくそれらを調整したところまで明らかにボリュームがありました。サミットでは、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

ネストされた場合は避け、1つのインデントレベルを削除します。

電流:

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


1
このような「機能」がPythonから意図的に除外された理由を実際に指摘しているので、この回答が好きです。初心者のプログラミングを教えるとき、私は多くの人がif (foo = 'bar')の値をテストしようとしているときにこの間違いをするのを見てきましたfoo
ジョナサンクロス

2
@JonathanCross、この「機能」は実際には3.8で追加される予定です。控えめにそれが必要としてとして使用することはほとんどありません-しかし、少なくともそれは、プレーンではありません=
ジョン・ラRooy

@JohnLaRooy:例を見ると、「必要なだけ控えめに使用される可能性は低い」と思います。〜10の例のうち、実際にコードを改善するのは2つだけであることがわかりました。(つまり、while条件での唯一の表現として、行の重複や本体でのループ条件の回避、またはネストを回避するためのelifチェーン)
AleksiTorhamo20年

三元で動作しませんa if a := 1 == 1 else FalseInvalid syntax:cしたがって、明らかにまだ古い厄介なことに頼る必要があります(lambda: (ret := foo(), ret if ret else None))()[-1] (つまりfoo()、三元での繰り返しの呼び出しを回避する方法として)
ハイエンジェル

39

いいえ、BDFLはその機能が気に入らなかった。

私が座っているところから、Guido vanRossumの「BenevolentDictatorFor Life」は、Pythonを可能な限りシンプルに保つために懸命に戦ってきました。彼が下した決定のいくつかに疑問を投げかけることができます。 'より頻繁に。しかし、Pythonを設計する委員会がなく、代わりに、主にメリットに基づいて、1人の設計者の感性をフィルタリングする信頼できる「諮問委員会」があったという事実は、素晴らしい言語の1つの地獄であるIMHOを生み出しました。


15
簡単ですか?この機能により、コードがよりコンパクトになり、読みやすくなるため、コードのかなりの部分が簡略化される可能性があります。今では、以前は1行必要だった2行が必要です。私は、Pythonが他のプログラミング言語が長年持っている機能を拒否した理由を理解できませんでした(そしてしばしば非常に正当な理由で)。特に、ここで説明しているこの機能は非常に便利です。
レジス2017年

6
少ないコードは、常に単純であるとは限らず、読みやすいとは限りません。再帰関数を例にとってみましょう。多くの場合、ループと同等の方が読みやすくなります。
FMF 2018

1
Cバージョンは好きではありませんif letが、if elifチェーンがあると、錆のようなものが本当に恋しいですが、それぞれの場合に条件の値を保存して使用する必要があります。
Thayne 2018

1
私が今書いているコード(私がこの問題を検索した理由)は、この機能がないとはるかに醜いです。ifの後に多くのelseifが続くのではなく、最後のelseの下にある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

できればこれを2回投票します。これは、このようなものが本当に必要なときに最適なソリューションです。ソリューションを正規表現Matcherクラスに適合させました。このクラスは一度インスタンス化され、ifステートメントで.check()が使用され、一致があればその本体内で.result()が使用されて一致が取得されます。ありがとう!:)
Teekin 2018

16

はい。ただし、Python3.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 2010

1
@MarkRansom全員がGuidoを歓迎します。..ため息。
StephenBoesch 2017

@javadba男は彼が間違っていたよりもずっと頻繁に正しいです。ビジョンを担当する人が1人いるだけで、委員会による設計よりもはるかに一貫性のある戦略が得られることを感謝します。私の主なパンとバターであ​​るC ++と比較対照することができます。
マークランサム2017

私はルビーとスカラ(v異なる言語)の両方がPythonよりもはるかに正しくそれを
理解していると感じてい

4

Python 3.8の新機能のおかげで=、Adaのような代入演算子を使用していなくても、このバージョンからそのようなことを行うことが可能になります:=。ドキュメントからの例:

# 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

条件で割り当てが違法である理由の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
In [161]:l_empty == [] Out [161]:True In [162]:[] == [] Out [162]:Trueそれが理由だとは思わない
火山

== 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)*
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.