暗黙の変換ができないのはなぜですか?


14

私が理解しているように、暗黙的な変換はエラーを引き起こす可能性があります。

しかし、それは理にかなっていない-通常の変換もエラーを引き起こすべきではありませんか?

なぜ持っていない

len(100)

次のように解釈する(またはコンパイルする)言語で動作する

len(str(100))

特に、それが機能する唯一の方法(知っている)だからです。言語はエラーが何であるかを知っています、なぜそれを修正しませんか?

この例ではPythonを使用しましたが、これほど小さいものは基本的に普遍的だと感じています。


2
perl -e 'print length(100);'3.

2
したがって、言語の性質とその型システム。それはpythonの設計の一部です。

2
それはあなたの想いを知らないので、それを修正しません。多分あなたは完全に異なる何かをしたかった。ループを訴えるようなものですが、以前はプログラミングのようなことはしませんでした。そのため、ユーザーが自分で修正した場合、ユーザーは自分が間違っていたことも、実装が期待どおりに動作しないこともわかりません。
ザイビス

5
@PieCrust Pythonはどのように変換することになっていますか?すべての可能な変換が同じ結果を返すことは事実ではありません
バクリウ

7
@PieCrust:文字列と配列は反復可能です。strintを反復可能に変換する暗黙的な方法はなぜでしょうか?どうrange?またはbinhexoct)またはchr(またはunichr)?strこの状況であなたにとって最も明白なように思えても、それらはすべてイテラブルを返します。
njzk2

回答:


37

何が価値があるために、len(str(100))len(chr(100))およびlen(hex(100))すべて異なっています。Pythonには整数から文字列への複数の異なる変換があるため、これを機能させる唯一の方法でstrはありません。もちろん、そのうちの1つが最も一般的ですが、それがあなたが意図したものであることは言うまでもありません。暗黙の変換とは、文字通り「言うまでもなく」という意味です。

暗黙的な変換の実際的な問題の1つは、いくつかの可能性がある場合にどの変換が適用されるかが常に明らかではないことです。これにより、読者は正しい暗黙的な変換を理解できないため、コードの解釈エラーが発生します。誰もが彼らが意図したものは「明白な解釈」であると常に言います。それが彼らの意図したものだからです。他の人には明らかでないかもしれません。

これが(ほとんどの場合)Pythonが暗黙よりも明示を好む理由であり、それを危険にさらさないことを好む理由です。Pythonが型強制を行う主なケースは算術です。それは可能に1 + 1.0選択肢が一緒に暮らすには余りにも迷惑になるので、それが許可されていません1 + "1"、それはあなたが意味するかどうかを指定する必要があります考えているのでint("1")float("1")ord("1")str(1) + "1"、または何か他のもの。結果タイプを選択するルールを定義するように、結果タイプを選択するルールを定義する(1,2,3) + [4,5,6]こともできます、許可しません1 + 1.0

他の言語では意見が一致せず、多くの暗黙的な変換が行われています。それらが含まれるほど、それらは明らかになりません。朝食の前に、「整数プロモーション」と「通常の算術変換」に関するC標準の規則を覚えてみてください。


+1 len1つのタイプでのみ機能する開始前提がどのように根本的に欠陥があるかを示すために。
KChaloux

2
@KChaloux:実際、私の例strではchrhexすべて同じタイプを返します!私は、コンストラクタがintだけをとることができる別の型を考えていましたが、まだ何も思いつきませんでした。理想的なのはXlen(X(100)) == 100;-) numpy.zerosが少しあいまいなコンテナです。
スティーブジェソップ

2
:可能性のある問題の例については、この参照docs.google.com/document/d/...destroyallsoftware.com/talks/wat
イェンスSchauder

1
暗黙の変換は、(私はこれが強制と呼ばれていると思います)持つような厄介なものを引き起こす可能性がありますa == bb == cしかし、a == c本当のことではありません。
-bgusach

素晴らしい答え。私が言いたかったことはすべて、より良い言葉だけでした!私の唯一の些細な点は、JavaScript強制ルールを記憶したり、暗黙のコンストラクタを不注意に使用してC ++コードベースに頭を包むのに比べて、C整数の昇格は非常に単純なことです。
-GrandOpener

28

私が理解しているように、暗黙的な変換はエラーを引き起こす可能性があります。

あなたは言葉がありません:暗黙の変換はランタイムエラーを引き起こす可能性があります

あなたが示すような単純な場合、それはあなたが何を意味したかはかなり明確です。しかし、言語はケースでは機能しません。彼らはルールを扱う必要があります。他の多くの状況では、プログラマーがエラーを(間違ったタイプを使用して)行ったか、プログラマーが意図したとおりにコードを実行するつもりだったかどうかは明確ではありません。

コードが間違っていると仮定すると、ランタイムエラーが発生します。それらを追跡するのは退屈なので、多くの言語は、あなたが台無しにしたことを伝え、コンピュータにあなたが本当に何を意味したかを伝えさせます(バグを修正するか、明示的に変換する)。他の言語も推測できます。そのスタイルは、デバッグが簡単で迅速かつ簡単なコードに適しているためです。

注意すべきことの1つは、暗黙的な変換によってコンパイラが少し複雑になることです。サイクルにもっと注意する必要があります(AからBへのこの変換を試してみましょう;うまくいきませんでしたが、BからAへの変換がありますそして、CとDサイズのサイクルについても心配する必要があります)暗黙的な変換を避ける小さな動機です。


ほとんどの場合、1つのタイプでしか機能しない関数について話していたため、変換が明らかになりました。複数のタイプで機能するものは何でしょうか。また、タイプによって違いが生じますか?print( "295")= print(295)。変数を除いて違いはありません。あなたが言ったことは、3D段落を除いて理にかなっています...繰り返していただけますか?
-Quelklef

30
@PieCrust-123 + "456"、 "123456"または579が必要でしたか?プログラミング言語はコンテキストを実行しません。したがって、追加が行われているコンテキストを知る必要があるため、「想像する」ことは困難です。どの段落が不明瞭ですか?
テラスティン

1
また、おそらくbase-10としての解釈は望んでいたものではありません。はい、暗黙の変換は良いことですが、エラーを隠せない場合に限ります。
デデュプリケーター

13
@PieCrust:正直に言うと、私は決してlen(100)戻ることを期待していません3。の表現のビット数(またはバイト数)を計算する方がはるかに直感的です100
user541686

3
私は@Mehrdadでよ、あなたの例から、それは明らかだあなたはその100%明白だと思うlen(100)気づかない間、あなたにしている意思をあなたに数100の10進数形式での文字の量を与える必要がありますしかし、それは実際に仮定のトンですそのうちの。16進表現のビット数、バイト数、または文字数(64-> 2文字)が返される理由を強力に説明できますlen()
-funkwurm

14

暗黙的な変換は非常に可能です。トラブルが発生する状況は、何かが機能する方法がわからない場合です。

この例は、Javascriptで見ることができます。この場合、+オペレーターはさまざまなタイミングでさまざまな方法で作業します。

>>> 4 + 3
7
>>> "4" + 3
43
>>> 4 + "3"
43

引数の1つが文字列の場合、+演算子は文字列連結です。それ以外の場合は加算です。

引数が与えられ、それが文字列なのか整数なのかわからず、それを追加したい場合は、少し混乱する可能性があります。

これに対処する別の方法は、基本的な遺産からのものです(perlの由来- プログラミングは難しい、スクリプトを書きましょう...を参照)

Basicでは、len関数は文字列で呼び出された場合にのみ意味を持ちます(Visual Basicのドキュメント:「任意の有効な文字列式または変数名。ExpressionがObject型の場合、Len関数はファイルに書き込まれるサイズを返します。 FilePut関数。」)。

Perlはこのコンテキストの概念に従います。JavaScriptに存在する混乱は+、perlでは演算子の型の暗黙的な変換が追加されたり連結されたりすることがあるため、perlでは発生しません。+常に加算と.常に連結。

スカラーコンテキストで何かが使用される場合、そのスカラー(たとえば、リストをスカラーとして使用すると、リストはあたかもその長さに対応する数であるかのように動作します)。文字列演算子を使用する場合(eq等価性テスト、cmp文字列比較)、スカラーは文字列であるかのように使用されます。同様に、数学コンテキストで(==等式テストおよび<=>数値比較のために)何かが使用された場合、スカラーは数値であるかのように使用されます。

すべてのプログラミングの基本的なルールは、「人を一番驚かせるようなことをする」ことです。これは、そこに驚きがないことを意味するものではありませんが、その努力はその人を少なくとも驚かせることです。

perl-phpの親coに行くと、オペレーターが文字列または数値のコンテキストで何かを実行できる状況があり、その振る舞いは人々にとって驚くべきものです。++オペレータはその一例です。数値では、期待どおりに動作します。などの文字列に作用すると、文字列を"aa"インクリメントします($foo = "aa"; $foo++; echo $foo;prints ab)。また、ロールオーバーするため、az増分するとになりbaます。これはまだ特に驚くことではありません。

$foo = "3d8";
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";

イデオネ

これは印刷されます:

3d8
3d9
3e0
4

暗黙の変換と同じ文字列で異なる動作をする演算子の危険性へようこそ。(Perlはそのコードブロックを少し異なる方法で処理します- 演算子が適用されると、最初から数値であると判断し"3d8"、すぐに++進み4ます(ideone)-この動作はperlop詳しく説明されています:自動インクリメントおよび自動デクリメント

さて、なぜある言語が何らかの方法で何かをし、別の言語が別の方法でそれを行うのかが、デザイナーの設計思想に到達します。Perlの哲学は、複数の方法があります -そして、これらの操作のいくつかを行ういくつかの方法を考えることができます。一方、Pythonには、PEP 20-The Zen of Pythonに記載されている哲学があります。

これらの設計の違いにより、言語が異なります。Pythonで数値の長さを取得する方法は1つあります。暗黙の変換は、この哲学に反します。

関連資料RubyでFixnumをStringに暗黙的に変換できないのはなぜですか?


私見、良い言語/フレームワークには、一般的なコーナーケースを異なる方法で処理する複数の方法が必要です(1000のプログラムで1000回よりも、言語またはフレームワークで1回、一般的なコーナーケースを処理する方が良い)。2人のオペレーターがほとんど同じことをしているという事実は、悪い振る舞いと見なされるべきではありません。2つの数値型変数を比較することは困難であってはならないxy...同値関係をもたらすか、ランク付けするようなAの方法ではなく、IIRC Pythonの比較演算子
supercat

...同値関係もランキングも実装せず、Pythonは便利な演算子を提供しません。
supercat

12

ColdFusionはこれのほとんどを行いました。暗黙の変換を処理するための一連のルールを定義し、単一の変数タイプを用意しました。

結果は完全な無秩序であり、6に「4a」を追加すると6.16667になります。

どうして?さて、2つの変数の最初は数値なので、結果は数値になります。「4a」は日付として解析され、「4 AM」と見なされます。午前4時は4:00/24:00、つまり1日の1/6(0.16667)です。6に追加すると、6.16667が得られます。

リストにはデフォルトのコンマ区切り文字があります。そのため、コンマを含むリストにアイテムを追加した場合、2つのアイテムを追加しただけです。また、リストはひそかに文字列であるため、1つのアイテムのみが含まれている場合、日付として解析されます。

文字列比較では、最初に両方の文字列を日付に解析できるかどうかをチェックします。日付タイプがないため、それを行います。数字、数字、8進表記、ブール値を含む文字列についても同じです( "true"および "yes"など)。

高速で失敗してエラーを報告するのではなく、代わりにColdFusionがデータ型変換を処理します。そして、良い方法ではありません。

もちろん、DateCompare ...のような明示的な関数を呼び出すことで暗黙的な変換を修正できますが、暗黙的な変換の「利点」を失います。


ColdFusionの場合、これは開発者を支援する方法です。特に、これらの開発者全員がHTMLに慣れている場合(ColdFusionはタグで動作し、CFMLと呼ばれます。後で、<cfscript>同様にスクリプトを追加しました。すべてのタグを追加する必要はありません)。そして、何かを動作させたいだけのときにうまく機能します。しかし、より正確な方法で物事を行う必要がある場合は、間違いである可能性があるものに対して暗黙的な変換を拒否する言語が必要です。


1
本当に「完全無政府状態」です!
hoosierEE

7

暗黙の変換は、呼び出しの前にintを文字列に変換するint a = 100; len(a)ことを明らかに意味するなど、曖昧でない操作には良いアイデアであると言っています。

しかし、これらの呼び出しは構文的に明確であることを忘れていますがa1、それら文字列であるを渡そうとしているプログラマーによって作成されたタイプミスを表している可能性あります。これは不自然な例ですが、変数名のオートコンプリートを提供するIDEでは、これらの間違いが発生します。

型システムは、エラーを回避することを目的としており、より厳密な型チェックを選択する言語では、暗黙的な変換によってそれが損なわれます。

==によって実行されるすべての暗黙的な変換でJavascriptの問題を確認してください。多くの人がno-implicit-conversion ===演算子を使用することを推奨しています。


6

あなたの声明の文脈をちょっと考えてみてください。これが「唯一の方法」だと言いますが、本当にそうなると本当に確信していますか?これはどうですか:

def approx_log_10(s):
    return len(s)
print approx_log_10(3.5)  # "3" is probably not what I'm expecting here...

他の人が述べたように、非常に具体的な例では、コンパイラがあなたの意図を直観することは簡単に思えます。しかし、より複雑なプログラムの大規模なコンテキストでは、文字列を入力するのか、ある変数名を別の変数名に入力するのか、間違った関数と呼ばれるのか、または他の多数の潜在的な論理エラーのいずれかがはっきりしないことがよくあります。

インタプリタ/コンパイラがあなたの意図を推測する場合、それは魔法のように機能することもありますが、それ以外の場合は、明確な理由なく機能しない何かをデバッグすることに何時間も費やします。平均的なケースでは、文が意味をなさないと言われ、実際の意味に合わせて数秒修正する方がはるかに安全です。


3

暗黙的な変換は、作業するのが非常に面倒な場合があります。PowerShellの場合:

$a = $(dir *.xml) # typeof a is a list, because there are two XML files in the folder.
$a = $(dir *.xml) # typeof a is a string, because there is one XML file in the folder.

突然、暗黙的な変換を行わない場合の2倍のテストと2倍のバグが必要になります。


2

明示的なキャストは、意図を明確にするために重要です。まず、明示的なキャストを使用すると、コードを読んでいる人にストーリーを伝えることができます。彼らは、あなたがあなたがしたことを意図的にしたことを明らかにします。さらに、コンパイラにも同じことが当てはまります。たとえば、C#では次のものは違法です。

double d = 3.1415926;
// code elided
int i = d;

キャストにより、精度が低下しますが、これは簡単にバグになる可能性があります。したがって、コンパイラはコンパイルを拒否します。明示的なキャストを使用することで、コンパイラーに次のように伝えますそして、彼は行きます:「オーケー!」そしてコンパイルします。


1
実際、私は暗黙的なキャストよりも、ほとんどの明示的なキャストを嫌います。私見、唯一の(X)y意味は「YはXに変換可能だと思う。可能な場合は変換を行い、そうでない場合は例外をスローする」ことです。変換が成功した場合、type yからtypeへの変換に関する特別な規則を知らなくても、結果の値が何になるかを予測できるはずXです。-1.5および1.5を整数に変換するように要求された場合、一部の言語では(-2,1)が生成されます。一部(-2,2)、一部(-1,1)、および一部(-1,2)。Cのルールはほとんどあいまいではありませんが
...-supercat

1
...その種の変換は、キャストよりもメソッドを介してより適切に実行されるように思われます(。
スーパーキャット

あなたはコンパイラーに「私は何をしているのか知っていると思う」と言っています。
gnasher729

@ gnasher729それにはいくつかの真実があります:)
ポール・ケルチャー

1

暗黙の変換/キャスト、または時々参照される弱いタイピングをサポートする言語は、意図した動作や期待した動作と必ずしも一致しないと仮定します。言語の設計者は、特定の種類の変換または一連の変換に対して行ったのと同じ思考プロセスを持っている可能性があります。その場合、問題は発生しません。ただし、そうしなかった場合、プログラムは失敗します。

ただし、失敗は明らかな場合もあれば、そうでない場合もあります。これらの暗黙のキャストは実行時に発生するため、プログラムは問題なく実行され、プログラムの出力または結果を見るときにのみ問題が表示されます。明示的なキャストを必要とする言語(これを厳密な型指定と呼ぶこともあります)では、プログラムが実行を開始する前にエラーが発生します。

先日、私の友人が2歳の子供に20 + 20とは何であるかを尋ね、2020年に返事をしました。

Javascriptの場合:

20+20
40

20+"20"
"2020"

したがって、暗黙のキャストが作成できる問題と、なぜ修正できないのかを確認できます。私が主張する修正は、明示的なキャストを使用することです。

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