
2026/08/26 6:39
Python の事前宣言定数は少し奇妙です
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Python は 6 つのプリデークレードされた項目を持っています:
True, False, None, __debug__, Ellipsis(...), および NotImplemented。これらはしばしば「定数」と呼ばれますが、その挙動は大きく異なります。
このうち 4 つ(
True, False, None, __debug__)は通常の識別子ではなく特別な構文的トークンです。そのため:•
x.True などの式は SyntaxError を発生させます。• 他の文脈では構文上問題がないにもかかわらず、これらに直接代入または削除を行うことは
SyntaxError を引き起こします(例:__debug__ = 67 または del __debug__)。• オブジェクト上の属性経由でのアクセス(例:
obj.__debug__)は無効ではありませんが、builtins 内のエントリを変更する(getattr/setattr を通じて)ことは、構文的トークンの挙動には影響しません。
対照的に、
Ellipsis と NotImplemented は通常のビルトイン関数に近い動作を示します:• 代入によってグローバルにシャドウ化されることが可能です(例:
NotImplemented = 67)。•
builtins 内の値を変更してもモジュールレベルでの名前には影響しますが、構文的トークン(... または NotImplemented という識別子)そのものには変更はありません。
さらに、
__debug__ は通常 True ですが、Python を -O フラグで実行すると False になります。これら 6 つの項目を区別する exact な設計理由、特になぜ 4 つだけが構文的トークンとして特別扱いされ、残りの 2 つは通常のビルトインとして扱われるのかは、現在も不明です。本文
Python 内置“定数”の真実は何か?
Python に先定義された定数として一般的に言及されるものが 6 つあります。しかし、それらの振る舞いは微妙に異なります。
存在する「定数」一覧
以下の 6 つが標準的な「定数」として認識されています:
TrueFalseNone__debug__
(または同等な記法Ellipsis
)...NotImplemented
1. True、False、および None
これらは キーワード です。しかし、それらは識別子ではなく、単なる構文解析段階で認識される記号(lexical token)に過ぎません。
特徴と挙動
- 通常、名前の解決はネーム解決プロセスで行われるため、レクサー自体での処理は稀です。
といった式はx.True
を引き起こします(これは言語仕様上の特殊なケースです)。SyntaxError- これらには直接アクセスする際、通常の識別子とは異なる挙動を示します。
2. debug
__debug__ は通常 True ですが、実行オプション -O でビルドすると False に切り替わります(デバッグビルドと非デバッグビルドの違い)。
唯一の特別ケース:割り当て不可
__debug__ は識別子として機能しますが、言語内で割り当てることのできない唯一の識別子です。
>>> __debug__ = 67 SyntaxError: cannot assign to __debug__ >>> x.__debug__ = 67 SyntaxError: cannot assign to __debug__
キーワードではないが特別な扱い
True、False、None と異なりキーワードではないため、属性として削除することもできません(ただし x が未定義の場合は NameError):
>>> del __debug__ SyntaxError: cannot delete __debug__ >>> del x.__debug__ Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'x' is not defined
「SyntaxError は嘘」について
__debug__ に割り当てようとする構文は実際には無効ではなく、エラーが返るケースは稀です。例えば assert 演算子内では以下のように振る舞います:
>>> assert (__debug__ := 67) # デバッグビルド実行時: SyntaxError が発生 # -O オプション実行時: アサーションはコンパイルされず、例外は発生しない
同様に、関数外部で
yield や await を使用する場合も同様です:
>>> assert (yield) >>> assert (await 67)
3. Ellipsis と NotImplemented
これらは「本当の意味での定数」ではなく、単なるビルトイン関数や変数のような扱いです。
グローバル変数として上書き可能
通常のグローバル変数と同様に値を変更可能です:
>>> NotImplemented = 67 >>> NotImplemented 67
4. 定数の上書きと builtins モジュール
True、False、None も構文トークンですが、同時に builtins モジュール内のビルトインオブジェクトとしても存在します。
getattr は使えないが、setattr は使える!
直接アクセスはできませんが、
builtins を介して値を書き換えることは可能です。
>>> import builtins >>> getattr(builtins, 'True') True >>> setattr(builtins, 'True', 67) >>> getattr(builtins, 'True') 67
しかし、構文解析トークンとしてアクセスした値は変わりません:
>>> True True
debug の独特な挙動
__debug__ も同様に builtins を介して変更可能です:
>>> setattr(builtins, '__debug__', 67) >>> builtins.__debug__ 67 >>> __debug__ True
この結果、
__debug__ は ある意味では真の定数(値を変更しても構文上の意味は変わらない)と言えます。
Ellipsis と NotImplemented は真の定数ではない
一方、
Ellipsis と NotImplemented は上記のように単純に上書きされます:
>>> setattr(builtins, 'Ellipsis', 67) >>> Ellipsis 67
ただし、構文記法
... の値は変わりません:
>>> ... Ellipsis
つまり、
... は真の定数ですが、その名前である Ellipsis はそうではありません。