Python、Go、Rust が三項演算子を使用しない理由
Lukas Schneider
DevOps Engineer · Leapcell

プログラミングでは、条件付きの判断を行い、その結果に基づいて異なるコードブロックを実行する必要がよくあります。多くのプログラミング言語では、これを行う最も一般的な方法は三項演算子を使用することです。しかし、Python は三項演算子をサポートしていません。興味深いことに、最も人気のある新興言語の 2 つである Go と Rust もサポートしていません。
なぜ Python は三項演算子をサポートしていないのでしょうか?この記事では主に、Python の条件構文の設計の背後にあるプロセスを分析し、なぜ独自の実装を採用したのかを説明します。同時に、他の言語が従来の三項演算子を放棄した理由も検証します。
三項演算子とは?
三項演算子は通常 ?:
を指し、構文は condition ? expression1 : expression2
の形式です。条件が真の場合、expression1
に評価され、そうでない場合は expression2
に評価されます。
簡略化された形式 a ? b : c
は、「条件 a
が真の場合、b
を返し、そうでない場合は c
を返す」と読むことができます。
三項演算子は標準的な if-else 構造の簡略版であり、条件チェックを実行して単一のステートメントで値を返すためによく使用されます。
// 通常の if-else if (a > b) { result = x; } else { result = y; } // 簡略化されたバージョン result = a > b ? x : y;
多くのプログラミング言語が C、C#、C++、Java、JavaScript、PHP、Perl、Ruby、Swift など、この構文設計を採用しています。間違いなく、これは長らくプログラミング言語における主流の設計アプローチでした(そして今日もそうです)。
この構文は非常に簡潔で効率的であり、可読性も高い(慣れれば)ため、非常に人気があります。
しかし、欠点がないわけではありません。Python はこの設計アプローチに対する最も有名な挑戦者です。Python がなぜ異なる道を選んだのかを探ってみましょう。
Python コミュニティの投票
Python は 1991 年にリリースされましたが、その後の 15 年間、条件付きの if-else 構文しか持っておらず、三項演算子や他の条件式をサポートしていませんでした。2006 年に条件式が導入されるまで、コミュニティは長くて複雑な議論を交わしました。これは決定するのが難しい構文機能だったと言えます。
当初、if-then-else(三項)式を追加するよう繰り返し要求されたため、2003 年 2 月に PEP 308 – 条件式 が提案されました。その目標は、コミュニティのほとんどがサポートできるソリューションを選択することでした。
ほどなく、コミュニティ内でいくつかの提案が浮上しました。何もしないことを好む少数のグループを除いて。
(1) 句読点ベースの三項演算子
これは従来の三項演算子で、前述の構文と同じです。
<condition> ? <expression1> : <expression2>
この提案は非常に人気があり、一部の開発者は実装コードを提出しました。しかし、Guido は 2 つの理由で却下しました。コロンは Python ですでに多くの用途があったこと(疑問符は一致するコロンを必要とするため、あいまいさはない可能性が高いですが)、C のような言語に慣れていない人にとって、この構文は理解しにくい可能性があることです。
(2) 既存および新しいキーワードで構築
新しい then
キーワードを導入し、既存の else
と組み合わせます。
<condition> then <expression1> else <expression2>
その利点には、明瞭さ、括弧の必要がないこと、既存のキーワードのセマンティクスを変更しないこと、ステートメントと間違われるリスクが低いこと、コロンのオーバーロードがないことなどがあります。短所は、新しいキーワードを追加すると、実装の点でコストがかかることです。
(3) その他の提案
これらは 2 番目のアプローチと精神的には似ていますが、それほど多くの支持を得られませんでした。
(if <condition>: <expression1> else: <expression2>) <condition> and <expression1> else <expression2> <expression1> if <condition> else <expression2> cond(<condition>, <expression1>, <expression2>)
特に、(if <condition>: <expression1> else: <expression2>)
形式は、通常の if-else 構文のフラット化されたバージョンであり、理解しやすいですが、括弧が必要です。これにより、ジェネレーター式との間で混乱が生じる可能性があり、インタープリターによるコロンの特別な処理が必要です。
また、<expression1> if <condition> else <expression2>
も注目に値します。これは、PEP 308 の初期バージョンで最初に推奨された提案でした。しかし、条件が最初に来ないため、この形式を嫌う人もいました。さらに、expression1
が長い場合、条件全体を見落としがちです。
当時投票されたすべてのオプションを以下に示します。
A. x if C else y
B. if C then x else y
C. (if C: x else: y)
D. C ? x : y
E. C ? x ! y
F. cond(C, x, y)
G. C ?? x || y
H. C then x else y
I. x when C else y
J. C ? x else y
K. C -> x else y
L. C -> (x, y)
M. [x if C else y]
N. ifelse C: x else y
O. <if C then x else y>
P. C and x else y
Q. any write-in vote
一般的に、開発者は何らかの形の if-then-else 式を導入したいと考えていましたが、投票後、明確な多数を占めるオプションはありませんでした。紛争は、句読点を使用するか、キーワードを再利用するか、括弧を再利用するか、新しいキーワードを導入するか、新しい構文を追加するか、という点に帰着しました...
投票が細分化されすぎたため、PEP は当時拒否されました。PEP は、「 Python の設計原則の 1 つは、不確実な状況に直面した場合、現状を維持することである」と述べています。
条件選択に and-or
を使用することの問題点
上記の投票イベントは 2004 年 3 月に行われましたが、このトピックに関する議論は PEP 308 が拒否された後も下火になりませんでした。開発者は、依然として if-else
に代わる簡潔な方法を探していました。
2005 年 9 月、メーリングリストで、Python 3.0 の and
および or
演算子のロジックを変更することが提案されました。このアイデアは、and
と or
を簡略化して、最後に評価されたオペランドを返す代わりに、常にブール値を返すようにすることでした。
この提案の理由は、著者が <condition> and <expression1> or <expression2>
の形式を使用して条件選択を実行したためでした。ただし、この点に関する Python の動作は他の言語とは異なり、不注意に使用するとバグにつながる可能性があります。
次の 2 つの例を見てください。どのような結果を期待しますか?
a = True and True or "Python" b = True and False or "Python"
<condition> and <expression1> or <expression2>
の場合、条件が false の場合、直接評価して expression2
を返します。条件が true の場合、expression1
を評価します。expression1
も true の場合、expression2
は評価されません。ただし、expression1
が true でない場合、expression2
を評価して返します。
したがって、上記の例では、a
は True
に評価され、b
は "Python"
になります。
Python は真偽値を特別な方法で処理します。たとえば、そのメールの著者は、expression1
が複素数 0+4i
であり、真偽値で False
と評価される状況に遭遇しました。その結果、最終的な戻り値は意図した expression1
ではなく、expression2
でした!
より良い解決策が導入されるまで、and-or
パターンは条件選択を実行するための比較的一般的な方法でした。PEP 308 はこのアプローチにも言及し、expression1
が false に評価される場合、問題が発生する可能性があることを指摘しました。このパターンを醜くて紛らわしいとさえラベル付けしました。
そのメールは、条件構文に関するコミュニティの議論を再燃させ、著名な開発者が意見を述べ始めました。
今日の視点から見ると、開発者が if-else
の現状に不満を持っていることは明らかです。ただし、一般的な and-or
回避策は十分ではなかったため、Python がこの問題を解決するために新しい標準構文を導入することを強く望んでいました。
ユニークな条件式
10 日間のメールディスカッションの後、Guido van Rossum は最終的に構文 X if C else Y
を持つ条件式を導入することを決定しました。その結果、PEP 308 が再開および更新され、その機能は翌年の Python 2.5 で実装されました。
これは、条件が最初に来ないため、一部の人々が不快に感じた以前に述べた構文です。
では、なぜこれが最終的に勝利した設計になったのでしょうか?それが最適な選択だったのでしょうか?
否定できないのは、決定的な要因は Guido 自身だったということです。コミュニティ投票で 1 年半前に多数の合意が得られなかったため、彼は BDFL(終身慈悲深き独裁者)としての権力を行使して、彼が最良のソリューションと考えるものを選択しました。
X if C else Y
は非常に理解しやすく、可読性が高いです。潜在的に紛らわしい句読点の代わりに、直感的で自然言語のような if-else
を使用することにより、「明示は暗黙より優れている」という Python のスタイルに従います。これは、&&
や ||
などの記号の代わりに and
や or
を使用する Python の使用法に似ています。
この構文の調整された順序に慣れるには時間がかかるかもしれませんが、実装には大きな利点があります。まず、新しいキーワード then
や when
、またはその他の構文要素を導入せずに、既存のキーワード if
と else
のみを再利用します。また、(if <condition>: <expression1> else: <expression2>)
形式よりも面倒ではありません。
次に、X if C else Y
の有効性を検証するために、Guido は標準ライブラリでの and-or
の組み合わせのすべての使用法を確認し、C and X or Y
の各インスタンスを X if C else Y
に置き換えることができることを発見しました。標準ライブラリの状態は、新しい構文が実行可能であることを証明しました。
この歴史を振り返ると、明確なスレッドを特定できます。Python が ?:
三項演算子を採用しなかったのは、主にそれが言語の明瞭さと率直さの重視と一致しないためです。代わりに、and-or
パターンの落とし穴を排除するために X if C else Y
式を導入しました。この設計は簡潔で読みやすく、非常に役立ちます。
全体として、Python の設計者は可読性と保守性を非常に重視しています。三項演算子を採用せずに、代わりに新しい条件式を作成することは、オープンな議論、慎重な評価、およびバランスの取れたトレードオフの結果でした。
なぜ Go と Rust は三項演算子をサポートしないのか?
Python の設計上の決定事項を探求した後、「反対陣営」で最も人気のある 2 つの言語を見てみましょう。
まず、Go。Go 言語の公式 FAQ には、具体的な質問「Go に ?:
演算子がないのはなぜですか?」が含まれています。
Go は ?:
演算子をサポートしておらず、代わりにネイティブの if-else
構文を使用することを推奨しています。ドキュメントには、1 つの段落で簡単な説明が記載されています。
Go には
?:
演算子がありません。これは、設計者がそれを使用して紛らわしい複雑な式を作成するのを見てきたためです。if-else
は長いですが、間違いなくより明確です。言語に必要な条件制御フロー構造は 1 つだけです。
次に Rust。その公式ドキュメントでは、三項演算子がサポートされていない理由を説明していないようです。しかし、さらに調査したところ、興味深い裏話があることがわかりました。2011 年 6 月に、Rust は実際に三項演算子を 導入しました (#565
) が、半年以内に設計者はそれが冗長であることに気づき、削除しました (#1698
、#4632
)!
なぜ Rust では三項演算子が冗長と見なされたのでしょうか?他の多くの言語とは異なり、Rust の if
は ステートメント ではなく、式 であるためです。これは、if
式の結果を直接変数に代入できることを意味します。
// 条件が true の場合、5 を取得します。そうでない場合は、6 を取得します let number = if condition { 5 } else { 6 };
この構文はシンプルで明確で、使い慣れた if-else
を代入に使用するのと同じです。それを三項演算子に置き換えることは、不必要であり、過剰ですらあります。
さらに、Rust は中括弧を使用してコードブロックを定義するため、中括弧内のコンテンツには複数の式を含めることができ、次の例のように改行もサポートします。
let x = 42; let result = if x > 50 { println!("x is greater than 50"); x * 2 // これは式であり、その値は `result` に代入されます } else { println!("x is less than or equal to 50"); x / 2 // これも式であり、返されて `result` に代入されます };
この種の使い方は Python では不可能です。最も重要な違いは、Rust の if
が ステートメント ではなく、式 であるという事実にあります。
2 つの概念の違いは次のとおりです。
- 式: 変数、定数、演算子などで構成されるコードの一部であり、値を評価し、他の式またはステートメント内で使用できます。
- ステートメント: 代入、条件、またはループなどのアクションを実行する単一の命令または命令のグループ。ステートメントは値を返さず(または何も返さず)、式で使用できません。
Rust の他に、if
が式であるプログラミング言語があり、Kotlin、Scala、F#、Swift などがあります。理論的には、これらの言語も三項演算子を必要としません。(余談:Swift は例外です。三項演算子を持っています。Kotlin にも ?:
演算子がありますが、2 つの記号が接続されています。たとえば、val result = a ?: b
は、a
が null でない場合は result
に代入し、それ以外の場合は b
を代入することを意味します。)
これらの言語レベルの設計の違いにより、Rust と Python/Go は本質的に異なる出発点から三項演算子の問題に取り組んでいます。この違いを理解すると、プログラミング言語についてより明確な視点が得られます。
したがって、元の質問に戻ります。一部のプログラミング言語が主流の三項演算子の構文を採用しないのはなぜですか?
?:
が簡潔で便利な設計であることは否定できません。ただし、句読点を使用することの欠点は、それが過度に抽象的になり、if-else
よりも可読性が低くなる可能性があることです。さらに、異なる言語の哲学と使用習慣が異なる選択肢につながります。
Python は、多くの紆余曲折を経て、最終的に独自の条件式構文を考案しました。Go は、三項演算子をサポートしないことを明示的に述べています。Rust はそれを導入してから削除しました。それは主に、if
を式として基本的に使用していたためです。
Rust プロジェクトのホスティングに最適な Leapcell をお選びください。
Leapcell は、Web ホスティング、非同期タスク、および Redis 向けの次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、または Rust で開発します。
無制限のプロジェクトを無料でデプロイ
- リクエストや料金なしで、使用量に対してのみお支払いください。
比類のない費用対効果
- アイドル料金なしの従量課金制。
- 例:25 ドルで、平均応答時間 60 ミリ秒で 694 万件のリクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的な UI。
- 完全に自動化された CI/CD パイプラインと GitOps 統合。
- 実用的な洞察を得るためのリアルタイムのメトリックとロギング。
簡単なスケーラビリティと高性能
- 高い同時実行性を容易に処理するための自動スケーリング。
- 運用オーバーヘッドゼロ—構築に集中するだけです。
ドキュメントで詳細をご覧ください!
X でフォローしてください: @LeapcellHQ