グローバル変数が邪悪なのはなぜですか?[閉まっている]


120

私はの使用がglobalpython(およびプログラミング全般)で悪い習慣と見なされている理由を見つけようとしています。誰かが説明できますか?より多くの情報とのリンクもいただければ幸いです。


投票を再開する-私は質問を編集して、説明に焦点を当て、オフサイトのリソースから離れました。(私はそれが閉鎖された理由のを前提としますが、念のために悪い習慣についての質問をされた状態で行うには、それの何かは、まだ開いている悪い習慣についてのこれらの他の質問の比較:evalimport *文字列の連結変数id属性影を
wjandrea

回答:


156

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

ただし、グローバル定数は概念的にグローバル変数と同じではありません。グローバル定数は完全に無害です。Pythonでは両者の間の区別は慣例により、純粋である:CONSTANTS_ARE_CAPITALIZEDglobals_are_not

グローバル変数が悪い理由は、関数が隠された(明白ではない、驚くべき、検出が難しい、診断が難しい)副作用を可能にし、複雑さが増し、スパゲッティコードにつながる可能性があるためです

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

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

より深く行き、なぜ副作用がすべてであるか、そして他の多くの啓蒙的なことを知りたいなら、関数型プログラミングを学ぶべきです:


35

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

特にpythonの場合、グローバルの可視性はモジュールに限定されているため、プログラム全体に影響を与える「真の」グローバルはありません。これにより、害が少なくなります。別のポイント:がないconstため、定数が必要な場合はグローバルを使用する必要があります。

私の実務では、関数内でグローバルを変更した場合、次のglobalように技術的にそれが必要ない場合でも、常にで宣言します。

cache = {}

def foo(args):
    global cache

    cache[args] = ...

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


3
多くの点で、Pythonのモジュールはシングルトンクラスに似ており、モジュールグローバルはクラスプロパティに似ています。
Corley Brigman、2013年

9
@CorleyBrigman:シングルトンクラスは、通常グローバルに起因する同じ問題に実際に悩まされることがよくあります:)
Erik Kaplun

4
Pythonモジュールの可視性はモジュールに限定されません。これらはインタープリター全体で使用でき、(ここで重要なことです)変更はインタープリター全体に影響します。インスタンスを作成するのは文字列とは異なります...すべての文字列インスタンスを変更するようなものです。サルのパッチングの匂い。
2014年

2
ほとんどのモジュールは、定数を除いてグローバル定義することから始まりません。グローバルが悪いということは、定数ではなく変数 / グローバル状態を意味します
BlackJack 2017

2
グローバルの使用は恐ろしい考えです。1つの理由は、「どこかに」存在する任意の辞書を更新する関数を適切にテストできないことです。グローバルを含むコードベースは、実際に機能することを証明できません。
TomaszSosiński2017年

10

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

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

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

クリーンで(ほぼ)バグのないコードには、可能な限り純粋な関数が必要です(純粋関数を参照)。純粋な関数とは、次の条件を持つ関数です。

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

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

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

(おそらく)FIRST原則に違反するユニットテスト

あなたはユニットテストと第一の原則(考慮すればさらにその上、Fの ASTテスト、I ndependentテスト、R epeatable、S ELF-検証およびT imely)は、おそらく(インディペンデントは、原則としてテスト違反するテストが依存していないことをどの手段お互いに)。

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

定数としてのグローバル

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

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


1
私は「定数としてのグローバルが問題である」のが好きです...オブジェクト指向の設計をしているのであれば、それは本当にそうです。IdCreatorクラス以外の誰かがID_LENを知る必要があるのはなぜですか?
Erik Aronesty 2018年

3

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

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