Pythonの名前空間とスコープ

:::note warn
2020年に書いた個人的なまとめです
:::

モジュール や パッケージ化する理由...保守しやすい とか 凝集度が高まる とかいくつか効用もありますが...
効用の1つに "名前空間で区切る" ことがある。
名前空間で区切ることでオブジェクトの衝突が起こらない。

# 名前空間に関して

定義を確認
https://docs.python.org/ja/3.9/tutorial/classes.html#python-scopes-and-namespaces

> 名前空間とは "名前空間 (namespace) とは、名前からオブジェクトへの対応付け (mapping) です。
> 名前空間について知っておくべき重要なことは、異なった名前空間にある名前の間には全く関係がないということです。
> 例えば、二つの別々のモジュールの両方で関数 maximize という関数を定義することができ、定義自体は混同されることはありません"

↓

"名前空間 (namespace) とは、名前からオブジェクトへの対応付け (mapping) です" 

(私はこれで解釈している)
**グローバルな名前空間 = モジュール内にある名前とオブジェクトの対応付けの集合**
グローバルな名前空間に定義されてるものを `globals()` で見ることができる。
https://gist.github.com/nnashiki/3457d2a12d357157891ed162086a05b2

**ローカルな名前空間 = 現在の空間にある名前とオブジェクトの対応付けの集合**
(関数、クラスが作る空間という説明がされること多いが、モジュールのトップレベルでlocal()するとglobal()と一致するので"その場所その場所"と表現している)
ローカルな名前空間に定義されてるものを `locals()` で見ることができる。
https://gist.github.com/nnashiki/8e08ae6d4bb2ad2566d3772f29418eaa

# スコープに関して
名前空間 と スコープ は大体セット

(私はこれで解釈している)
(現在の)モジュールトップレベルの要素に直接アクセス可能な領域をグローバルスコープ↓↓

![スクリーンショット 2021-02-04 23.01.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/cea18073-f8a5-0d8e-d51b-3dc78f6286dc.png)


コードのその場所その場所の要素に直接アクセス可能な領域をローカルスコープ↓↓

![スクリーンショット 2021-02-04 23.03.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/c2bbed57-3d44-c7ce-af6d-46a66c10fbe9.png)


----

※ 公式docの定義は以下です

> スコープ (scope) とは、ある名前空間が直接アクセスできるような、 Python プログラムのテキスト上の領域です
> "直接アクセス可能" とは、修飾なしに (訳注: spam.egg ではなく単に egg のように) 名前を参照した際に、その名前空間から名前を見つけようと試みることを意味します。

※ 公式docの定義が難しいので atmarkit.co.jp さんの解釈を混ぜています

> 「ある名前空間が直接アクセスできるような」というのは「ある名前空間で定義されている要素に直接アクセス可能」といった意味合いだ。

by https://www.atmarkit.co.jp/ait/articles/1612/09/news030_2.html

↓
↓　atmarkit.co.jp さんが解釈した定義
↓

"スコープとはある名前空間で定義されている要素に直接アクセス可能なPythonプログラムの領域です"


ex. 別の名前をつける例 (こうすれば競合しない)

```
from os import open as osopen
from gzip import open as gzipopen
```

ex.  (こうなっていまうと後勝ち)

```
from os import open
from gzip import open
```

- "組み込みの名前が入った名前空間は Python インタプリタが起動するときに作成され、決して削除されることはありません。"
    - そのため、組み込みとは競合するオブジェクトを定義してはいけない


## スコープの種類
### ローカル スコープ
  
- "最初に探される、最も内側のスコープは、ローカルな名前を持っています。"
- locals() で参照できる (名前空間はdictで実装されている)

```
$ python3
Python 3.8.6rc1 (v3.8.6rc1:08bd63da6e, Sep  7 2020, 16:14:12) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
```

### enclosing スコープ

- "外側の(enclosing)関数のスコープは、近いほうから順に探され、ローカルでもグローバルでもない名前を持っています。
- 関数がネストした時の話
  
### グローバル スコープ
- "現在のモジュールのグローバルな名前を持っています。"
- モジュール内のトップレベルに定義したら、そのモジュールの中では参照することができる
  - パッケージ や モジュール分けすれば衝突しないということ
  
- "モジュールのグローバルな名前空間は、モジュール定義が読み込まれたときに作成されます。"
- "通常、モジュールの名前空間は、インタプリタが終了するまで残ります。"
    - これを活かして、web server の応答をキャッシュできたりする

### ビルトインスコープ
- "一番外側の(最後に検索される)スコープはビルトイン名を持っています。"
  - 一番最後に検索されるのでビルトイン名と同じ名前を定義しないように気をつけよう
    - PyCharm なら予約語を定義した際に警告してくれる   
- `dir(__builtins__)` でビルトインの中身を見ることができる
  - https://docs.python.org/ja/3/library/builtins.html#module-builtins
  - https://docs.python.org/ja/3/library/functions.html#built-in-funcs
  - https://docs.python.org/ja/3/library/constants.html#built-in-consts

### おまけ
- "Jupyter Notebookなどのセル（に入力する変数定義など）は「__main__」という名前のモジュールに含まれることになっている"
  - [［Python入門］関数のローカル変数とスコープ (3/3)：Python入門 - ＠IT](https://www.atmarkit.co.jp/ait/articles/1905/24/news019_3.html)
    - 対話モードも同じ
- 名前空間のwikipediaの定義
  - > 名前の集合を分割することで衝突の可能性を低減しつつ参照を容易にする概念
    - https://ja.wikipedia.org/wiki/%E5%90%8D%E5%89%8D%E7%A9%BA%E9%96%93 の プログラミングセクションの定義