web-dev-qa-db-ja.com

なぜグローバル変数は悪なのですか?

python(および一般的なプログラミング)でglobalの使用が悪い習慣であると考えられる理由を説明する良いソースを見つけようとしていました。誰かが私を指し示すか、ここで説明できますか?

92
LarsVegas

これはPythonとは関係ありません。グローバル変数はどのプログラミング言語でも悪いです。

ただし、グローバル定数は概念的にはグローバル変数と同じではありません;グローバル定数は完全に無害です。 Pythonには強制された違いはなく、慣例によってのみCONSTANTS_CAPITALIZEDglobals_are_notがあります。

それらが悪い理由は、関数が隠された(非自明、驚くべき、検出しにくい)副作用を可能にし、複雑さの増加につながり、潜在的に スパゲッティコード につながるためです。

ただし、アルゴリズムの最適化、複雑さの軽減、キャッシングとメモ化、または主に命令型のコードベースに由来する移植構造の実用性のいずれかのために、関数型プログラミングにおいてもグローバル状態の適切な使用は許容されます(ローカル状態と可変性)。

全体として、あなたの質問には多くの方法で答えることができるので、最善の策は「なぜグローバル変数が悪いのか」をグーグルすることです。いくつかの例:

深く掘り下げて、副作用がすべてである理由や他の多くの啓発的なことを知りたい場合は、関数型プログラミングを学ぶ必要があります。

123
Erik Allik

はい、理論上、グローバル(および「状態」一般)は悪です。実際には、pythonのpackagesディレクトリを調べると、そこにあるほとんどのモジュールが多数のグローバル宣言で始まっていることがわかります。明らかに、人々は彼らに問題はありません。

特にPythonの場合、グローバルの可視性はモジュールに制限されているため、プログラム全体に影響する「真の」グローバルはありません。もう1つのポイント:constがないため、定数が必要な場合はグローバルを使用する必要があります。

私の練習では、関数のグローバルを変更した場合、技術的に必要がない場合でも、常にglobalで宣言します。

cache = {}

def foo(args):
    global cache

    cache[args] = ...

これにより、グローバルの操作を追跡しやすくなります。

28
georg

このトピックに関する個人的な意見は、グローバル変数が関数ロジックで使用されていると、他のコードがロジックとその関数の予想出力を変更し、デバッグが非常に難しくなり(特に大きなプロジェクトで)、テストが難しくなることです同じように。

さらに、コードを読んでいる他の人(オープンソースコミュニティ、同僚など)を検討する場合、グローバル変数が設定されている場所、変更された場所、このグローバル変数に何を期待するのかを理解しようとするのに苦労します関数定義自体を読み取ることでその機能を決定できる分離された関数。

(おそらく)純粋関数の定義に違反している

クリーンで(ほぼ)バグのないコードには、できるだけ純粋な関数が必要だと思います( 純粋な関数 を参照)。純関数とは、次の条件を持つ関数です。

  1. 関数は、常に同じ引数値が与えられると、同じ結果値を評価します。関数の結果値は、プログラムの実行中またはプログラムの異なる実行間で変化する可能性のある隠された情報や状態に依存することも、I/Oデバイスからの外部入力に依存することもできません。
  2. 結果の評価は、可変オブジェクトの変更やI/Oデバイスへの出力など、意味的に観察可能な副作用や出力を引き起こしません。

グローバル変数を持つことは、外部コードとして両方が予期しない結果を引き起こす可能性がある場合、上記の少なくとも1つに違反しています。

純粋関数の別の明確な定義:「純粋関数は、すべての入力を明示的な引数としてを取り)、すべての出力を明示的な結果として生成する関数です。」 [1] 。グローバル変数を持つことは、入力と出力の1つ(グローバル変数)が明示的に指定または返されないため、純粋な関数の概念に違反します。

(おそらく)F.I.R.S.T原則に違反する単体テスト

さらに、単体テストと最初の原則(Fastテスト、Independentテストを考慮する場合、Reeatable、Self-ValidatingおよびT単に)おそらく独立したテストの原則に違反します(つまり、テストは相互に依存しません)。

グローバル変数(常にではない)を持つことは、ほとんどの場合(少なくともこれまで見てきたこと)、結果を準備して他の関数に渡すことです。これもこの原則に違反しています。グローバル変数がそのように使用されている場合(つまり、関数Xで使用されるグローバル変数を関数Yで最初に設定する必要がある場合)、関数Xを単体テストするには、最初に関数Yをテスト/実行する必要があります。

定数としてのグローバル

一方、他の人々がすでに言及したように、グローバル変数が「定数」変数として使用される場合、言語は定数をサポートしないため、わずかに改善できます。ただし、私は常にクラスで作業し、クラスメンバーとして「定数」を持つことを好み、グローバル変数をまったく使用しません。 2つの異なるクラスがグローバル変数を共有する必要があるコードがある場合、おそらくソリューションをリファクタリングし、クラスを独立させる必要があります。

グローバルを使用すべきではないと思います。しかし、それらを使用する場合、作成者は、よりクリーンでほぼバグのないコードのために、いくつかの原則(おそらく上記で言及したもの、および他のソフトウェアエンジニアリングの原則と優れた実践)を考慮する必要があります.

9
Rafael

それらは不可欠であり、画面が良い例です。しかし、マルチスレッド環境または多くの開発者が関与している場合、実際にはしばしば問題が発生します。誰が(誤って)設定またはクリアしたのでしょうか?アーキテクチャによっては、分析に費用がかかり、頻繁に必要になる場合があります。グローバル変数の読み取りは問題ありませんが、書き込みは、たとえば単一のスレッドまたはスレッドセーフクラスによって制御する必要があります。したがって、グローバル変数は、それ自体が悪と見なされる結果によって発生する可能性のある高い開発コストの恐れを生じさせます。したがって、一般的に、グローバル変数の数を低く保つことをお勧めします。

3