したがって、引数としてメソッドに送信するコード内のすべての数値は、マジックナンバーと見なされますか?私には、そうすべきではありません。いくつかの数字がユーザー名の最小長であるとしましょう。コードで「6」を使用し始めます...それからメンテナンスの問題があり、ここで「6」は魔法の数字です...引数の1つが、たとえばコレクションのi番目のメンバーとして整数を受け入れるメソッドを呼び出している場合、そのメソッド呼び出しに「0」を渡すと、この場合、「0」が魔法として表示されません数。どう思いますか?
したがって、引数としてメソッドに送信するコード内のすべての数値は、マジックナンバーと見なされますか?私には、そうすべきではありません。いくつかの数字がユーザー名の最小長であるとしましょう。コードで「6」を使用し始めます...それからメンテナンスの問題があり、ここで「6」は魔法の数字です...引数の1つが、たとえばコレクションのi番目のメンバーとして整数を受け入れるメソッドを呼び出している場合、そのメソッド呼び出しに「0」を渡すと、この場合、「0」が魔法として表示されません数。どう思いますか?
回答:
数字の意味が文脈で非常に明確な場合、それは「魔法の数字」の問題だとは思わない。
例:文字列の部分文字列を最初からあるトークンまで取得しようとしているとしましょう。コードは次のようになります(想像上の言語とライブラリ)。
s := substring(big_string, 0, findFirstOccurence(SOME_TOKEN, big_string));
このコンテキストでは、数値0の意味は十分に明確です。定義START_OF_SUBSTRING
して0に設定できると思いますが、この場合はやり過ぎだと思います(ただし、サブストリングの開始が0でないことがわかっている場合は正しいアプローチですが、それはあなたの状況)。
別の例は、数値が偶数か奇数かを判別しようとしている場合です。書き込み:
isEven := x % 2;
以下ほど奇妙ではありません:
TWO := 2;
isEven := x % TWO;
負の数をテストする
MINUS_ONE := -1;
isNegativeInt := i <= MINUS_ONE;
また、私には奇妙に感じます、私はむしろ見たいです
isNegativeInt := i <= -1;
360
ほとんどの人がそれが何を意味するかを理解していることを理解して完全な回転をマークするなどの数字を使用するのが妥当です定数を指定しても問題ない場合)
0
私のサブストリングの例のコンテキストでも置き換えると思います。その場合、これは彼らが引き起こす可能性のある損害の最小量かもしれません。幾何学的計算を行うコーディングを行ってから長い時間が経ちましたが、一般的に、値15、30、45、60、90、180、360は受け入れられた定数でした。私は誰が定義した見たことがないFIFTEEN_DEGREES
、...
何かが定数宣言であるべきかどうかを決定する際に、3つの重要な要素を提案します。
数値リテラルは不必要に冗長、不必要に不正確、またはその両方になりやすいため、piのようなものはおそらく数値リテラルとしてではなく、名前付き定数として記述する必要があります。キャッシュ内のスロット数のようなものはおそらく名前付き定数である必要があります(ただし、以下の注を参照)。if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;
式の方がほぼ確実に読みやすいので、ステートメント内の数字「4」、「28」、「29」のようなものはおそらく定数と名付けるべきではありませんif ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;
。標準の管理者は、その年の2100年2月の長さが上記の式と一致しないことを示していることに注意してください。 そのような日付を正しく処理するための障害(つまり、コードが整数オーバーフローまたは他のそのような問題によってトリップすることはありません)。
ルール#2の重要な注意点は、コードが名前付き定数で簡単に表現できない方法でハードコードされた数値に依存する場合があることです。たとえば、離散パラメーターとして渡される2つのベクトルの外積を計算する方法は、3次元ベクトルで使用する場合にのみ意味があります。必要な次元数は、ルーチンを完全に書き直さずに有意に変更できる値ではありません。3つの4次元ベクトルの外積を計算する必要性を予見したとしても、値 "3"に名前付き定数を使用しても、その必要性を満たせるようになることはほとんどありません。
これは、すべての原則として、程度の問題です。一般的に、ソースコードの数値リテラルは、サイズが大きいほど疑わしいです。10のような最大長または0x587FB0のようなメモリアドレスは明らかに悪い習慣です-遅かれ早かれこれらの値を複数回繰り返さなければならないことはほぼ確実です。かわった。
0はスケールのもう一方の端にあります。まだ疑わしいが、それほど多くはない。センチネル値として0を使用していますか?次に、おそらく定数がその意味を説明できるという理由だけで、代わりにシンボリック定数を使用する必要があります。「0は成功を意味する」など、非常に定着した文化的合意ですか?おそらく大丈夫です。「コレクションの最初のアイテム」という意味ですか?それは無害かもしれませんが、first()
私のような代替方法があるなら、おそらくそれを好むでしょう。
コンテキストからすぐに明らかにならない名前のない数字はすべてマジックナンバーです。コンテキストからすぐに明らかになる意味を持つ数字を定義するのは少しばかげています。
django(python web framework)では、次のような生の番号でデータベースフィールドを定義できます。
firstname = models.CharField(max_length=40)
middlename = models.CharField(max_length=40)
lastname = models.CharField(max_length=40)
言うよりも明確です(そして推奨されるプラクティス)
MAX_LENGTH_NAME = 40
...
firstname = models.CharField(max_length=MAX_LENGTH_NAME)
middlename = models.CharField(max_length=MAX_LENGTH_NAME)
lastname = models.CharField(max_length=MAX_LENGTH_NAME)
長さを変更する必要はほとんどないので(そして常にmax_length
フィールドの長さと比較できます)。アプリケーションを最初にデプロイした後にフィールドの長さを変更する必要がある場合、djangoコードのフィールドごとに正確に1つの場所で変更し、さらにDBのスキーマを変更するための移行を記述する必要があります。max_length
オブジェクトのタイプの定義済みフィールドを参照する必要がある場合、直接行うことができます-それらのフィールドがPerson
クラスを定義している場合Person._meta.get_field('firstname').max_length
、max_length
使用されている(1つの場所で定義されている)。同じ40が複数のフィールドに使用されたという事実は、それらを個別に変更する場合があるため、無関係です。firstnameの長さは、middlenameまたはlastnameの長さに決して依存しないでください。これらは個別の値であり、独立して変更できます。
多くの場合、配列インデックスは名前のない数字を使用できます。python辞書に入れたいデータのCSVファイルがあり、行の最初の要素を辞書としてkey
記述した場合のように:
mydict = {}
for row in csv.reader(f):
mydict[row[0]] = row[1:]
もちろん、名前index_column = 0
を付けて次のようなことをすることができます。
index_col = 0
mydict = {}
for row in csv.reader(f):
mydict[row[index_col]] = row[:index_col] + row[index_col+1:]
またはさらに悪いことに、after_index_col = index_col + 1
を取り除くように定義しますindex_col+1
が、それは私の見解ではコードを明確にしません。また、index_col
名前を付けると、列が0でない場合でもコードが機能するようになります(そのためrow[:index_col] +
)。
max_lngth=40
対がmax_length=MAX_LENGTH_NAME
ある古典的なマジックナンバーのexamlple 悲鳴象徴します。45文字の名前をサポートしたい日が来ますが、今では「40」の使用はすべて疑わしく、慎重に調べる必要があります。
40
に1
。コンテキストを考える必要があります。