ProgrammingPython開発者

Pythonにおける「LEGBルール」とは何ですか?変数名を検索する際の動作を説明してください。誤解が生じた場合に発生する非自明なバグの例を挙げてください。

Hintsage AIアシスタントで面接を突破

回答。

LEGBルールは、Pythonにおける名前空間(スコープ)の検索ルールです。この略語は以下のように解釈されます:

  • Local — ローカル名前空間(関数内);
  • Enclosing — すべての内包関数の名前空間;
  • Global — モジュールの名前空間(グローバル変数);
  • Builtin — Pythonのビルトイン名(len, list, など)。

Pythonが変数を見つけると、まずローカルで探し、次に内包された関数で探し、その後モジュールのグローバル領域で探し、最後にビルトインオブジェクトの名前空間で探します:

def outer(): x = 'enclosing' def inner(): x = 'local' print(x) inner() outer() # 'local'

inner()内でxへの代入を削除すると、Pythonはouterの内包スコープからxを取得します。もしそこにも変数がなければ、グローバルで、次にビルトインから探します。


ひねりのある質問。

変数の外部スコープを変更する際に nonlocal または global を宣言し忘れた場合、関数の動作はどう変わりますか?

回答: 外部スコープの変数を内包関数内で nonlocal(またはモジュール内にある場合は global)を宣言せずに変更しようとすると、Pythonは新しいローカル変数を作成し、外部変数は変更されません。

例:

x = 10 def foo(): x = 20 # これは新しいローカルxで、グローバルには影響しない foo() print(x) # 10

グローバルxを変更するためには:

def foo(): global x x = 20

外部関数の変数については、nonlocalを使用してください。


このトピックの微妙な点を知らなかったために実際に発生したエラーの例。


ストーリー1

教育プロジェクトで呼び出し回数をカウントするために内包関数を使用しました。開発者は次のように書きました:

def counter(): count = 0 def inc(): count += 1 return count return inc

しかし、inc()内でcountが新しいもの(ローカルcountが作成される)と見なされたため、UnboundLocalErrorが発生しました。解決策:countをnonlocalとして宣言すること。


ストーリー2

Webアプリケーションで、ハンドラー内でグローバルに設定されたキャッシュを変更したいと考えました:

cache = {} def add_to_cache(key, value): cache[key] = value

しかし、もし関数内で完全にキャッシュを書き換えようとした場合(例えば cache = {key: value} のように)、新しいローカルcacheが作成され、グローバルなものとは無関係になりました。これはglobal cacheが不足していたためです。


ストーリー3

大規模なETLシステムでバグが発生しました。プログラマは関数内でresult変数を明示的に初期化せず、外部スコープから取られると期待していました。しかし、数回の反復の後、結果が狂い始めました。なぜなら、内部でresult += valueが実行され、result = result + value(nonlocal/globalなし)になるからです。これは各呼び出しでresultが再初期化されてしまったからです。