{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c05cff8b-e68e-482d-9400-4e97ab1e9a5b",
   "metadata": {},
   "source": [
    "# Python 入門講座　第11回: 名前って何？　What's in a name?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b410bb7-a2f8-4fc9-ab93-972734f2fed7",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "プログラムを作成する際には、変数や関数、クラス、モジュールなど様々な名前が現れます。\n",
    "これらの名前はその有効範囲(`スコープ`）の中では一意的に定まっている必要があります。\n",
    "\n",
    "名前の有効範囲を把握しておかないと、プログラムが意図しない動作をする場合も考えられます。\n",
    "(プログラムの違う場所で、同じ名前を違う意味／目的で使っていると、思わぬ動作をすることがあります。）\n",
    "\n",
    "pythonプログラム中で導入された**名前**の有効範囲は一定のルールで定まっています。\n",
    "ある時点での有効な名前の集合（辞書）を`名前空間`と呼んだりします。\n",
    "（実際には、`名前空間`と`スコープ`は区別なく使われることがあるかもしれません。）\n",
    "\n",
    "pythonではモジュール(プログラムファイル）、関数定義、クラス定義によって新たな名前空間が作られます。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cad25c9-3866-4462-aae7-5e06fec74853",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## 名前のスコープの例（関数定義）\n",
    "\n",
    "いま名前のスコープの例として、二つの数の和を返す関数を考えて見ます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a1387812-ae26-4a88-9288-7494dd08414e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "from logging import info,warning, getLogger, INFO\n",
    "getLogger().setLevel(INFO)\n",
    "\n",
    "def add2(x:int, y:int = 1) -> int:\n",
    "    return x+y"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "183ebd0c-5260-438b-a540-b33aa4304fdd",
   "metadata": {},
   "source": [
    "これを使って、次のpythonプログラムを実行したとします。\n",
    "``` python\n",
    "x=10; y=20 ; Y = 100\n",
    "print(f\"global {add2(3)=} , {x=}, {y=}\") \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab7e68dc-457d-4b6d-b426-fba15fa4a584",
   "metadata": {},
   "source": [
    "add2(3)の値は　`10 + 20 = 30` or `10 + 1 = 11` or  `3 + 1 =  4` or  `3 + 20 = 5`のいずれになるでしょうか？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f70d8776-037f-4fb0-b5c6-420ae204eeab",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "実際に試して見ましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f992b558-493c-41ba-9ed1-77316cc419e9",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root: add2(3) = 4 , x = 10, y = 20\n"
     ]
    }
   ],
   "source": [
    "def add2(x:int, y:int = 1)->int:\n",
    "    return x+y\n",
    "\n",
    "x = 10; y = 20 ; Y = 100\n",
    "logging.warning(f\" {add2(3) = } , {x = }, {y = }\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a499268-6689-4833-af8d-5540200f2b0f",
   "metadata": {},
   "source": [
    "結果は、\n",
    "\n",
    "``` python\n",
    "add2(3) = 4 , x = 10, y = 20\n",
    "```\n",
    "\n",
    "となります。関数定義の中の`x`や`y`は関数定義の外で定義された`x`や`y`とは無関係ということがわかります。\n",
    "このことを、関数定義の中の`y`と関数定義の外で定義された`y`は「異なるスコープを持つ」、\n",
    "あるいは「別の名前空間に属している」といいます。\n",
    "\n",
    "pythonでは、関数定義の中の`y`は関数`add2`の`local`な名前空間に属しています。\n",
    "一方, 関数定義の外で定義された`y`はこのプログラムの`global`な名前空間に属しています。\n",
    "\n",
    "さて、関数定義の中で`x + y`を`x + Y`と書き間違えた時、なにが起きるかを見てみましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fb473d02-7b55-4b52-8270-6e1aa55a96f1",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:in add2: x=3, y=1, Y=200\n",
      "INFO:root:global add2(3) = 203 x = 10 y = 20 Y=100 \n"
     ]
    }
   ],
   "source": [
    "from logging import info,warning, getLogger, INFO\n",
    "getLogger().setLevel(INFO)\n",
    "\n",
    "def add2(x:int, y:int = 1)->int:\n",
    "    Y=200\n",
    "    info(f\"in add2: {x=:}, {y=:}, {Y=:}\")\n",
    "    return x + Y # もし　 x + Yと書き間違えたとしたら？\n",
    "\n",
    "x=10;y=20;Y = 100\n",
    "info(f\"global {add2(3) = } {x = } {y = } {Y=} \")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a78918e8-a82f-4db9-9579-494555ec7584",
   "metadata": {},
   "source": [
    "このように、名前の有効範囲を正しく把握しておかないと、プログラムは動作するけれど、\n",
    "意図した正しい結果を返さない場合もあるということです。\n",
    "\n",
    "注意：PythonにはC/C++などのコンパイル言語と違って、変数名の宣言文はありません。\n",
    "そのため、実行時に意図せず同じ変数名を異なる意味で使った時にも、エラーにならない場合があります。\n",
    "`pylint`や`flake8`などのツールを使うことで、プログラム実行前にこれらのエラーの可能性をチェックできます。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a8c1b52-ea5e-4a9d-8748-3090d983f686",
   "metadata": {},
   "source": [
    "## [新しい名前が現れる場所](https://docs.python.org/ja/3.10/reference/executionmodel.html)\n",
    "\n",
    "先ほどは、関数定義のブロックの中に現れる変数名を例に、名前空間について説明しました。\n",
    "\n",
    "pythonで新しい名前が現れる(名前空間に新しい名前が追加される）場所は、次のようなものがあります。\n",
    "   \n",
    " * 代入が行われるときの代入対象の識別子 : 代入文　`x=1` の`x`, 代入式　`v:=2` の`v`　など\n",
    " * クラスや関数の定義  : `def func():` の`func` ,`class  c:`の`c`\n",
    " * 関数の仮引数 : `def func(x)`の`x`, `lambda z:print(z)`の`z`\n",
    " * for ループのヘッダ: `for i in (1,2,3)`の`i` \n",
    " * with 文や except 節の as の後ろ :`with open(\"file1\") as fin `の`fin`や`except UnboundLocalError as err`の`err`\n",
    " * import 文 : `import myModule` の `myModule` , `impolrt myModule as mm`の`mm`\n",
    " * from ... import 文：　`from mod import component`の`component`, `from mod import component as comp`の`comp`\n",
    "     * `from ... import *`では import されるモジュール内で定義されている, アンダースコアから始まるもの以外の全ての名前を束縛します。\n",
    "\n",
    "これらの場所では、新しい名前にオブジェクトを結びつけています(束縛). 新しい名前が束縛されたときその名前はその時の名前空間(local 名前空間)に登録されます。\n",
    "\n",
    "モジュール(~一つのpythonプログラムファイル)、関数定義、クラス定義は新しい名前空間を作成します。\n",
    "\n",
    "`global`名前空間はモジュールの名前空間と組み込み名前空間(`__builtins__`の名前空間）を合わせた名前空間です。\n",
    "組み込み名前空間には、pythonが標準的に提供する関数、定数、クラスなどの名前が登録されています。\n",
    "\n",
    "`__builtins__`名前空間に登録されている名前は、\n",
    "\n",
    "``` python\n",
    "dir(__builtins__)\n",
    "```\n",
    "\n",
    "で確認することができます。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc7162d7-706c-442c-81ac-0ff0d8808478",
   "metadata": {},
   "source": [
    "余談：\n",
    "\n",
    "Pythonプログラムにはシステムがあらかじめ定義している**語**として、`def`,`if`などプログラム言語としての文法要素である`予約語` と pythonがあらかじめ用意している定数や関数などの`組み込み`オブジェクト名があります。　`予約語`は文法規則で決まっているため変更することができませんが、`組み込み`の変数や関数の名前は、他の名前(識別子, identifier)と同じように、別のオブジェクトに割り当てる(束縛する）こともできます。　しかし、`組み込み`オブジェクトの束縛を変更することは理解しにくいプログラムを作る元になりますので、避けましょう。\n",
    "(`True`, `False`, `None`の三つの定数は予約後であると同時に、`__builtins__` 名前空間にも登録されています。）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "33c8603c-209c-42ee-b430-1e5d28205725",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root:lambda x = 10, locals() = {'x': 10}\n",
      "WARNING:root:global x = 1\n",
      "WARNING:root:exec_lambda x = 20, locals() = {'x': 20}\n",
      "WARNING:root:exec_global x = 1\n",
      "WARNING:root:global w = 9\n"
     ]
    }
   ],
   "source": [
    "x=1\n",
    "(lambda x:logging.warning(f\"lambda {x = }, {locals() = }\"))(10)\n",
    "logging.warning(f\"global {x = }\")\n",
    "\n",
    "exec('(lambda x:logging.warning(f\"exec_lambda {x = }, {locals() = }\"))(20)\\nlogging.warning(f\"exec_global {x = }\")')\n",
    "\n",
    "[w for i in range(10) if (w:=i)%3 == 0]\n",
    "logging.warning(f\"global {w = }\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49669e8e-3ac4-4da3-9a5d-aa2b7f105f3b",
   "metadata": {},
   "source": [
    "### 名前の解決\n",
    "\n",
    "ある名前がプログラム中で使われる時、この名前に結び付けられている(束縛されている）オブジェクトを見つける必要があります。\n",
    "Pythonでは`local()`の名前空間から出発し、`global()`までの階層的な名前空間を1段階ずつ登りながら検索します。\n",
    "`globals()`にもこの名前がみつからなければ、`Error`が生成されます。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "baf71db5-a5ce-43ee-866c-4e565f05e741",
   "metadata": {},
   "source": [
    "![変数のスコープと名前空間](./_images/ScopeAndNamespace.png)\n",
    "\n",
    "関数定義、クラス定義などのブロックはそれぞれの名前空間を持っています。\n",
    "一つのファイルに含まれるプログラムテキストはグローバルな名前空間を定義しています。\n",
    "プログラム中の名前はこの階層的な構造を持つ名前空間をlocalな名前空間からグローバル空間まで一段ずつ検索され、\n",
    "束縛されているオブジェクトを見つけます。\n",
    "\n",
    "pythonでは、関数定義のブロック、クラス定義のブロックの内部にクラス定義や関数定義を置けます。\n",
    "”関数プログラミング”のスタイルではこのような階層構造を反映して、`名前空間` もglobal,` locals`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec7da6ca-74b2-4ed8-be21-dc7c51b68fe2",
   "metadata": {},
   "source": [
    "## `dir()`関数\n",
    "\n",
    "`dir()` 関数は、引数なしで実行された時、実行時のスコープに含まれる名前のリストを返します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ef332d7d-a619-4f68-920f-a815b8f000ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['INFO', 'In', 'Out', 'Y', '_', '__', '___', '__builtin__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '__vsc_ipynb_file__', '_dh', '_i', '_i1', '_i2', '_i3', '_i4', '_i5', '_ih', '_ii', '_iii', '_oh', 'add2', 'exit', 'getLogger', 'get_ipython', 'info', 'logging', 'os', 'quit', 'sys', 'w', 'warning', 'x', 'y']\n"
     ]
    }
   ],
   "source": [
    "print(dir())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f9ee63c-9020-4712-b1f5-8a874eed3ea3",
   "metadata": {},
   "source": [
    "### globals()関数とlocals() 関数\n",
    "プログラムのある時点での`global`なスコープを持つ名前の辞書(名前空間）を`global()`関数呼び出しで、\n",
    "また`local`なスコープを持つ名前を`local()`関数呼び出しで確認できます。\n",
    "例をみて見ましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "20ebf6b7-fed0-4926-910b-7dde16ff3514",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root: in __main__: ['x', 'y'] locals()={'x': 3, 'y': 1}\n",
      "INFO:root:local  scope:[('x', 3), ('y', 1)]\n",
      "INFO:root:global scope:[('x', 1), ('y', 2)]\n",
      "INFO:root: in __main__: ['x', 'y'] locals()={'x': 3, 'y': 1}\n",
      "INFO:root:local  scope:[('x', 3), ('y', 1)]\n",
      "INFO:root:global scope:[('x', 1), ('y', 4)]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4 1 2\n",
      "4 1 4\n"
     ]
    }
   ],
   "source": [
    "def add2(x:int, y:int =1)->int:\n",
    "    info (f\" in {__name__}: {dir()} {locals()=}\")\n",
    "    info (f\"local  scope:{[e for e in locals().items()]}\")\n",
    "    info (f\"global scope:{[(k,v) for k,v in globals().items() if k in ('x','y')]}\")\n",
    "    return x+y\n",
    "x=1\n",
    "y=2\n",
    "print(add2(3), x, y)\n",
    "y=4\n",
    "print(add2(3), x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6cb50f6-d372-415b-b5e5-718e6da5840d",
   "metadata": {},
   "source": [
    "というように、`add2(3)`を実行している時の`local`スコープでは`('x', 3), ('y', 1)`などとなっています。\n",
    "計算にはこれらの`local`スコープの値が使われ、`global`スコープの値は影響を与えていません。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2bd7e15-c337-4767-a0b0-4a18db27cfdb",
   "metadata": {},
   "source": [
    "この関数定義で仮引数`y`の記述を忘れて、\n",
    "``` python\n",
    "def add2(x):\n",
    "    info (f\"local  scope:{[e for e in locals().items()]}\")\n",
    "    info (f\"global scope:{[(k,v) for k,v in globals().items() if k in ('x','y')]}\")\n",
    "    return x+y\n",
    "```\n",
    "としてしまった場合を考えて見ます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "bfe9e68a-a5d9-45fb-bb85-42525c8f6ed7",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:local  scope:[('x', 3)]\n",
      "INFO:root:global scope:[('x', 1), ('y', 2)]\n",
      "INFO:root:local  scope:[('x', 3)]\n",
      "INFO:root:global scope:[('x', 5), ('y', 6)]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5 1 2\n",
      "9 5 6\n"
     ]
    }
   ],
   "source": [
    "def add2(x):\n",
    "    info (f\"local  scope:{[e for e in locals().items()]}\")\n",
    "    info (f\"global scope:{[(k,v) for k,v in globals().items() if k in ('x','y')]}\")\n",
    "    return x+y\n",
    "x=1;y=2\n",
    "print(add2(3), x, y)\n",
    "x=5;y=6\n",
    "print(add2(3), x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f849859c-6e58-4f6e-b2b6-2ea29855439c",
   "metadata": {},
   "source": [
    "この場合には、関数`add2(3)`を実行する際の`local`名前空間には変数`y`がありません。pythonは名前空間をたどり、`global`空間にある名前`y`を見つけて、その値を使って関数の値を計算します。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1696f60-829d-4972-ac6c-b8763045db38",
   "metadata": {},
   "source": [
    "この例のように、関数の値が関数定義の外の変数の影響を受けて変わってしまうと、関数の動作の確認／検証を難しくしてしまいます。　一般的にはこのような関数の定義は避けるべきものとされています。\n",
    "しかしながら、問題によっては、関数外にシステムを特徴付ける数値(~定数）があって、それに基づいて処理を定義する\n",
    "ことを求められることがあります。このような場合にも、`global`宣言を使って関数定義外の変数を使っていることがを\n",
    "明確する必要があります。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d3cdc4b7-c980-4bf8-9267-5b611167f4e3",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:local  scope:[('x', 3)]\n",
      "INFO:root:global scope:[('x', 1), ('y', 10)]\n",
      "INFO:root:local  scope:[('x', 3)]\n",
      "INFO:root:global scope:[('x', 1), ('y', 10)]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 13 1 10\n",
      "1 4 13 1 10\n"
     ]
    }
   ],
   "source": [
    "def sumg(x):\n",
    "    global y\n",
    "    y=10\n",
    "    info (f\"local  scope:{[e for e in locals().items()]}\")\n",
    "    info (f\"global scope:{[(k,v) for k,v in globals().items() if k in ('x','y')]}\")\n",
    "    return x+y\n",
    "x=1\n",
    "y=2\n",
    "print(x,y, sumg(3), x, y)\n",
    "y=4\n",
    "print(x,y, sumg(3), x, y)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "02fe3e9f-6d4a-4fca-8c5a-ffc0aa1a7941",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:local  scope:[('y', 4), ('z', 4)]\n",
      "INFO:root:local  scope:[('x', 3), ('y', 4), ('z', 4)]\n",
      "INFO:root:global scope:[('x', 10), ('y', 20), ('z', 30)]\n",
      "INFO:root:local  scope:[('y', 10), ('z', 10)]\n",
      "INFO:root:local  scope:[('x', 20), ('y', 10), ('z', 10)]\n",
      "INFO:root:global scope:[('x', 10), ('y', 20), ('z', 30)]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7 10 20 30\n",
      "30 10 20 30\n"
     ]
    }
   ],
   "source": [
    "def incy(z):\n",
    "    y=z\n",
    "    info (f\"local  scope:{[e for e in locals().items()]}\")\n",
    "    def func(x):\n",
    "        nonlocal z\n",
    "        info (f\"local  scope:{[e for e in locals().items()]}\")\n",
    "        info (f\"global scope:{[(k,v) for k,v in globals().items() if k in ('x','y','z')]}\")\n",
    "        return (x+y)\n",
    "    return func\n",
    "x=10; y=20; z=30\n",
    "print(incy(4)(3), x, y, z)\n",
    "print(incy(10)(20), x, y, z)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee2f5ad7-126d-449e-a011-55a19eb42f17",
   "metadata": {},
   "source": [
    "### `global` 宣言と　`nonlocal`　宣言\n",
    "Pythonの名前の検索ルールから関数やクラスの中から一つ上のレベルの名前空間の変数に\n",
    "束縛されたオブジェクトを入手することがで来ます。\n",
    "しかし、この場合には内側の名前空間を持つブロックでは、このオブジェクトを変更することができません。\n",
    "この場合、`global`宣言や`nonlocal`宣言を使うことで、内側の名前空間からこれらの変数に束縛されたオブジェクトを入手できます。\n",
    "`global`,`nolocal` で宣言される変数は、宣言の文が実行される前にそれらの名前空間で定義（束縛）されている必要があります。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "1eb8acf5-752d-4b23-aabd-eb6f87267a2f",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root:x =1\n",
      "WARNING:root:x = 1, foo() = 1 , x = 1\n",
      "WARNING:root:x =1\n",
      "WARNING:root:x = 1, foo() = 1 , x = 1\n",
      "WARNING:root:x = 1,bah() =10 ,x = 1\n",
      "WARNING:root:__main__ local variable 'x' referenced before assignment\n",
      "WARNING:root:x = 1, gee() = None, x = 1\n",
      "WARNING:root:x = 1, huhu() = 2, x = 2\n"
     ]
    }
   ],
   "source": [
    "# global と local\n",
    "import logging\n",
    "\n",
    "x=1\n",
    "\n",
    "def foo(): # 代入文の左辺に現れない変数はglobalとして取り扱われる。\n",
    "    logging.warning(f\"{x =}\") \n",
    "    return x\n",
    "\n",
    "logging.warning(f\"{x = }, {foo() = } , {x = }\")\n",
    "\n",
    "def bah(): #代入文の左辺に現れる\n",
    "    x=10\n",
    "    return x\n",
    "\n",
    "def gee(): # 代入文の左辺に現れる変数を、代入前にその値を使おうとしている。\n",
    "    #print(globals())\n",
    "    try:\n",
    "        x=x+1 #　\n",
    "        return x\n",
    "    except UnboundLocalError as err:\n",
    "        logging.warning(f\"{__name__} {err}\")\n",
    "        return None\n",
    "\n",
    "def huhu(): # global宣言すると、その変数名はglobal空間の変数として扱われる。\n",
    "    global x\n",
    "    try:\n",
    "        x=x+1 #　\n",
    "        return x\n",
    "    except UnboundLocalError as err:\n",
    "        logging.warning(err)\n",
    "        return None\n",
    "\n",
    "logging.warning(f\"{x = }, {foo() = } , {x = }\")\n",
    "\n",
    "logging.warning(f\"{x = },{bah() =} ,{x = }\")\n",
    "\n",
    "logging.warning(f\"{x = }, {gee() = }, {x = }\")\n",
    "\n",
    "logging.warning(f\"{x = }, {huhu() = }, {x = }\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "628f47e3-6e7d-4b37-9860-67b57cd32a9e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n = 1, nl = 1\n",
      "n = 2, nl = 1\n",
      "add_1(10) =11, add_2(10) =12\n",
      "n = 1, nl = 2\n",
      "n = 2, nl = 2\n",
      "add_1(20) =21, add_2(20) =22\n"
     ]
    }
   ],
   "source": [
    "def gen_add_n(n):\n",
    "    def add_n(x):\n",
    "        nonlocal nl\n",
    "        nl +=1\n",
    "        print(f\"{n = }, {nl = }\",end=None)\n",
    "        return (x+n)\n",
    "    nl=0\n",
    "    return add_n\n",
    "\n",
    "add_1=gen_add_n(1)\n",
    "add_2=gen_add_n(2)\n",
    "\n",
    "print(f\"{add_1(10) =}, {add_2(10) =}\")\n",
    "print(f\"{add_1(20) =}, {add_2(20) =}\")\n",
    "if \"n\" in locals():print(n)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ffacc84-690a-4618-8e59-0d796f97f316",
   "metadata": {},
   "source": [
    "## 名前の付け方 ([PEP 8から][PEP8]) \n",
    "\n",
    "[PEP8]: https://pep8-ja.readthedocs.io/ja/latest/#section-20\n",
    "\n",
    "Pythonプログラムで使う名前では、ここまでで説明したように、名前の有効範囲に注意を払う必要があります。\n",
    "名前付の際に一定のルールを採用することで、名前の衝突をさけ、プログラムの予期せぬ動作を避けることも\n",
    "推奨されています。\n",
    "\n",
    "pythonで使われる名前についてのガイドラインがPEP(Python Enhancement Proposals)に示されています。\n",
    "完全にこれに従う必要はありませんが、見やすいPythonプログラムを作るための参考になるでしょう( [PEP8]の第2セクションもご覧ください)。\n",
    "\n",
    "多くの方に使われるであろうモジュールを公開する際には、この命名規則に準拠することが推奨されます。\n",
    "`pylint`などのpythonプログラム診断ツールを使って、この命名規則に対するチェックを行えます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ab1ad545-92e0-4b80-88f5-cb79b5e9b04c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function print in module builtins:\n",
      "\n",
      "print(...)\n",
      "    print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
      "    \n",
      "    Prints the values to a stream, or to sys.stdout by default.\n",
      "    Optional keyword arguments:\n",
      "    file:  a file-like object (stream); defaults to the current sys.stdout.\n",
      "    sep:   string inserted between values, default a space.\n",
      "    end:   string appended after the last value, default a newline.\n",
      "    flush: whether to forcibly flush the stream.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(print)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1bc0f81-4e6a-4cdc-be07-751f5b073714",
   "metadata": {},
   "source": [
    "### 実践されている命名方法の例\n",
    "PEP8の「命名規則」の中で列挙されている名前付の方法の例を見て見ましょう。\n",
    "\n",
    " * b (小文字1文字)\n",
    " * B (大文字1文字)\n",
    " * lowercase\n",
    " * lower_case_with_underscores\n",
    " * UPPERCASE\n",
    " * UPPER_CASE_WITH_UNDERSCORES\n",
    " * CapitalizedWords (CapWords, または **CamelCase** - 文字がデコボコに見えることからこう呼ばれます)。StudlyCaps という呼び名でも知られています。\n",
    "    * 注意: CapWords の中で頭字語(大文字だけで綴られる言葉。NATO、HTTPなど）を含める場合、頭字語の全ての文字を大文字にします。つまりこのやり方だと、HttpServerError より HTTPServerError の方が良いということになります。\n",
    " * mixedCase (はじめの文字が小文字である点が、CapitalizedWords と違います！)\n",
    " * Capitalized_Words_With_Underscores (醜い！)\n",
    "\n",
    "関連する名前の集まりに、短い一意なプレフィックスを付けるやり方もあります。Python ではこのやり方を多く使っているわけではありませんが、完全を期すために紹介しておきます。\n",
    "\n",
    "pythonプログラムの慣習として\"_\"(アンダースコア）で始まる(`_single_leading_underscore`) \n",
    "あるいは終わる(`single_trailing_underscore_`)名前には次のような意味が含まれています。\n",
    "\n",
    "#### \"\\_\"(アンダースコア）で始まる, あるいは終わる名前\n",
    "\n",
    " 1. 名前が一つの\"_\"で始まる名前(`_single_leading_underscore`)：　内部的に使われる名前(`non-public`)を意味します。 moduleを`from ... import *`構文で`import`する際には、これらの名前は`import`されません。\n",
    " 1. 名前が二つの\"_\"で始まる名前(`__double_leading_underscore`): クラスの属性名として使われた時、クラス内でのみ有効な属性名であることを意味します。(`名前のマングリング機構`によってクラス外からは**この名前ではアクセスすることはできません**。）\n",
    " 1. 名前が一つの\"_\"で終わる名前(`single_trailing_underscore_`): pythonのキーワードとの衝突を避けるために使われます。 例: `tkinter.Toplevel(master, class_='ClassName')`\n",
    " 1. 名前が二つの\"\\_\"で始まり、二つの\"\\_\"で終わる名前(`__double_leading_and_trailing_underscore__`): pythonがシステムで持っている \"マジック\"オブジェクト または \"マジック\"属性です。 この種類の名前を新たに定義することは、将来問題を引き起こす可能性がありますので、避けなければなりません。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6efba82e-0af9-4fae-b30c-e3341674f87f",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "## 守るべき命名規約\n",
    "PEP 8が述べている[「守るべき命名規約」](https://pep8-ja.readthedocs.io/ja/latest/#section-20) を簡単にまとめておきます(一部を省略しています）。\n",
    "\n",
    "### こんな名前は嫌だ\n",
    "単一の文字 'l' (小文字のエル)、'O' (大文字のオー)、'I'(大文字のアイ) を決して変数に使わないでください。\n",
    "\n",
    "フォントによっては、これらの文字は数字の1や0と区別が付かない場合があります。'l'(小文字のエル) を使いたくなったら、'L' を代わりに使いましょう。\n",
    "\n",
    "### ASCII との互換性\n",
    "***標準ライブラリ*** で使われる識別子は、ASCII と互換性がなければなりません(PEP 3131)。\n",
    "\n",
    "### パッケージとモジュールの名前\n",
    "\n",
    "モジュールの名前は、全て小文字の短い名前にすべきです。\n",
    "読みやすくなるなら、アンダースコアをモジュール名に使っても構いません。\n",
    "\n",
    "Pythonのパッケージ名は、全て小文字の短い名前を使うべきですが、アンダースコアを使うのは推奨されません。\n",
    "(パッケージとは、複数のモジュールを階層的にまとめたものを、あたかも一つのモジュールのように取り扱う仕組みです。）\n",
    "\n",
    "### クラスの名前\n",
    "\n",
    "クラスの名前には通常 CapWords 方式を使うべきです。\n",
    "\n",
    "主に callable として使われる、ドキュメント化されたインターフェイスの場合は、クラスではなく関数向けの命名規約を使っても構いません。\n",
    "\n",
    "Python にビルドインされている名前には別の規約があることに注意してください： ビルトインされている名前のほとんどは、単一の単語(または、二つの単語が混ざったもの) ですが、例外的に CapWords 方式が使われている名前や定数も存在しています。\n",
    "\n",
    "### 例外の名前\n",
    "\n",
    "例外はクラスであるべきです。よって、クラスの命名規約がここにも適用されます。\n",
    "しかし、その例外が実際にエラーである場合には例外の名前の最後に　\"Error\" をつけるべきです。\n",
    "\n",
    "### 関数や変数の名前\n",
    "\n",
    "関数の名前は小文字のみにすべきです。また、読みやすくするために、必要に応じて単語をアンダースコアで区切るべきです。\n",
    "\n",
    "変数の名前についても、関数と同じ規約に従います。\n",
    "\n",
    "mixedCase が既に使われている (例: threading.py) 場合にのみ、互換性を保つために mixedCase を許可します。\n",
    "\n",
    "### グローバル変数の名前\n",
    "\n",
    "(ここで言う「グローバル変数」はモジュールレベルでグローバルという意味だと思いたいですが) ここで示す規約は、関数レベルのものと同じです。\n",
    "\n",
    "`from M import *` 方式でimportされるように設計されているモジュールは、 グローバル変数をエクスポートするのを防ぐため __all__ の仕組みを使うか、エクスポートしたくないグローバル変数の頭にアンダースコアをつける古い規約を使うべきです (こうすることで、これらのグローバル変数は「モジュールレベルで公開されていない」ことを開発者が示したいかもしれません)。\n",
    "\n",
    "(モジュールのグローバル変数`__all__`にexportする名前(`str`)のリストを設定することで、`from ... import *`でimportされる名前を指定しておくことができます。）\n",
    "\n",
    "\n",
    "## 定数 の名前\n",
    "\n",
    "定数は通常モジュールレベルで定義します。全ての定数は大文字で書き、単語をアンダースコアで区切ります。例として `MAX_OVERFLOW` や `TOTAL` があります。\n",
    "\n",
    "\n",
    "### 関数やメソッドに渡す引数\n",
    "\n",
    "  * インスタンスメソッドのはじめの引数の名前は常に `self` を使ってください。\n",
    "\n",
    "  * クラスメソッドのはじめの引数の名前は常に `cls` を使ってください。\n",
    "\n",
    "  * 関数の引数名が予約語と衝突していた場合、アンダースコアを引数名の後ろに追加するのが一般的には望ましいです。衝突した名前を変更しようとして、略語を使ったりスペルミスをするよりマシです。よって、 `class_` は `clss` より好ましいです。 (多分、同義語を使って衝突を避けるのがよいのでしょうけど)\n",
    "\n",
    "### メソッド名とインスタンス変数\n",
    "\n",
    " * 関数の命名規約を使ってください。つまり、名前は小文字のみにして、読みやすくするために必要に応じて単語をアンダースコアで区切ります。\n",
    "\n",
    "  * **公開されていない**メソッドやインスタンス変数にだけ、アンダースコアを先頭に付けてください。\n",
    "\n",
    "  * サブクラスと名前が衝突した場合は、Python のマングリング機構を呼び出すためにアンダースコアを先頭に二つ付けてください。\n",
    "\n",
    "     * Python はアンダースコアが先頭に二つ付いた名前にクラス名を追加します（マングリング機構)。つまり、クラス `Foo` に `__a` という名前の属性があった場合、この名前は `Foo.__a` ではアクセスできません (どうしてもアクセスしたいユーザーは `Foo._Foo__a` とすればアクセスできます)。一般的には、アンダースコアを名前の先頭に二つ付けるやり方は、サブクラス化されるように設計されたクラスの属性が衝突したときに、それを避けるためだけに使うべきです。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
