+ 作るログイン
図を読み込んでいます…
1 / 26
JS から来た人の Python ― どこから来て、なぜ動くのか / 全19章

Python 05:関数 ― 引数は札、既定値の罠、スコープの探し方

発行
アニメモ公式
版
第1版
改訂
コマ
全26

JS の function と Python の def は何が同じで何が違うのか。呼び出したときに関数の中に名前の置き場ができ、引数を渡すことも「札を付けるだけ」だという様子を図で追います。デフォルト引数・キーワード引数・*args・**kwargs を JS の残余引数や分割代入と並べ、既定値に [] を書くと呼ぶたびに増える罠の正体、lambda、L・E・G・B の順に名前を探すスコープ、global と nonlocal、if や for がスコープを作らないこと、関数もオブジェクトであることまでを説明します。

処理に名前を付けてまとめたい

第4章の tenki では、摂氏を華氏に直す計算(t * 1.8 + 32)や、「暑い・ちょうどいい・寒い」の判定を、使う場所に直接書いていました。同じ計算をあちこちに書くと、直すときに全部を探して回ることになります。

そこで関数です。処理のまとまりに名前を付けて、何度でも呼べるようにします。考え方は JS とまったく同じで、書き方が少し違うだけです。

JSPython
関数を作るfunction toF(c) { … }def to_f(c): と字下げ
小さな関数(c) => c * 1.8 + 32lambda c: c * 1.8 + 32
名前の付け方toF(camelCase)to_f(snake_case。PEP 8)
呼ぶtoF(30)to_f(30)

def は define(定義する)の略です。ブロックの範囲は、第3章で見たとおり : と字下げで決まります。

この章では、書き方に加えて、関数を呼んだとき、名前とオブジェクトに何が起きているのかを、第4章の「札」の図で追いかけます。そうすると、JS から来た人がつまずきやすい「関数に list を渡したら元が変わった」「デフォルト引数に [] を書いたら、呼ぶたびに増えていく」「関数の中で外の変数に足そうとしたらエラーになった」が、全部同じ絵で説明できるようになります。

※2026年9月時点(Python 3.13)の情報です。

  • JS から来た人の Python 05 ― 関数
  • 1 / 26
  • 処理に名前を付けてまとめたい
  • JavaScript(Node.js)
  • Python
  • function toF(c) {
  • def to_f(c):
  • return c * 1.8 + 32;
  • return c * 1.8 + 32
  • }
  • const toF2 = (c) => c * 1.8 + 32;
  • to_f2 = lambda c: c * 1.8 + 32
  • console.log(toF(30)); // 86
  • print(to_f(30)) # 86.0
  • function の代わりに def(define の略)。{ } の代わりに、第3章と同じ : と字下げ
  • この章は、書き方に加えて「呼んだとき、名前とオブジェクトに何が起きるか」を図で追う
  • ひらめき
  • 摂氏から華氏への変換を、あちこちで書いていた。関数にすれば1か所に名前を付けてまとめられる。

def で作る。return が無ければ None を返す

関数は def 名前(引数): と書き、その下に字下げして本体を書きます。

python
def to_f(c):
    """摂氏を華氏にする"""
    return c * 1.8 + 32

本体の最初に置いた """…""" の文字列は docstring(ドックストリング)と呼ばれる関数の説明です。JS の JSDoc にあたりますが、Python ではただのコメントではなく、関数のオブジェクトに to_f.__doc__ としてしまわれ、help(to_f) やエディタの説明表示に使われます。

return で値を返すのも JS と同じです。違うのは、return を書かなかったときです。

JSPython
return が無い関数の戻り値undefinedNone
値を2つ返したい配列やオブジェクトで返すreturn lat, lon(tuple が返る)
受け取るconst [lat, lon] = f()lat, lon = f()

図の hello は print するだけで何も返さないので、r = hello("東京") の r は None になります。第3章で「Python の『無い』は None だけ」と書いたのは、このことも含んでいます。

return lat, lon と書くと、第4章で見たとおりカンマが tuple を作るので、2つの値が1つの tuple にまとまって返ります。受け取る側で lat, lon = f() と分ければ、複数の値を返したのと同じように使えます。

  • JS から来た人の Python 05 ― 関数
  • 2 / 26
  • def で作る。return が無ければ None を返す
  • def to_f(c):
  • JS
  • Python
  • """摂氏を華氏にする"""
  • return c * 1.8 + 32
  • 作る
  • function f() {}
  • def f():
  • def hello(name):
  • 返す
  • return x
  • return x
  • print("こんにちは", name)
  • 返さないと
  • undefined
  • None
  • r = hello("東京")
  • 説明
  • JSDoc
  • docstring
  • print(r) # None
  • 複数を返す
  • 配列で返す
  • tuple で返す
  • return lat, lon と書けば tuple が返り、lat, lon = f() で分けて受け取れる
  • ひらめき
  • def 名前(引数): と書き、字下げした中身が関数の本体。何も返さなければ None が返る。

def も代入の一種:関数に名前の札が付く

ここから、関数を呼んだときに何が起きているかを、札の図で追います。

まず、def の行そのものが何をしているかです。

python
def to_f(c):
    return c * 1.8 + 32
x = 30

def to_f を実行すると、Python は関数の中身を関数のオブジェクト(型は function)として1つ作り、to_f という名前の札をそれに付けます。図のとおり、x = 30 で 30 に札 x を付けるのと同じ形です。

行作られるオブジェクト付く札
def to_f(c): …function のオブジェクトto_f
x = 30int のオブジェクト 30x

図の緑の囲みは、このファイル全体で使える名前の置き場です。これをグローバル(そのファイルの一番外側)と呼びます。

「def も札を付けるだけ」と分かると、次のようなことも自然に理解できます。同じ名前で def を2回書くと、後の関数に札が付け替わり、前の関数は使えなくなります。JS の関数宣言の「巻き上げ(hoisting)」のような仕組みは無いので、def の行を実行する前に関数を呼ぶと NameError になります。Python のファイルは上から順に実行されるからです(第8章で、import のときにもこの「上から実行」が効いてきます)。

  • JS から来た人の Python 05 ― 関数
  • 3 / 26
  • def も代入の一種:関数に名前の札が付く
  • グローバル(ファイル全体の名前)
  • オブジェクト
  • to_f
  • to_f
  • function(関数)
  • x
  • 30
  • int
  • def to_f(c): return c * 1.8 + 32  /  x = 30
  • ひらめき
  • def to_f は、関数のオブジェクトを作って to_f という札を付ける。x = 30 と同じ形。

呼び出すと、関数の中の名前の置き場ができる

次に、関数を呼びます。

python
y = to_f(x)

to_f(x) を実行すると、Python はその呼び出し専用の名前の置き場を新しく作ります。図の黄色の囲みです。これをフレーム(またはローカルの名前空間)と呼びます。

そして、引数を渡します。引数を渡すとは、関数の中の名前 `c` の札を、渡されたオブジェクトに付けることです。図のように、c の札は、x が付いているのと同じ 30 に付きます。30 がコピーされるわけではありません。

順番起きること
1x が付いているオブジェクト(30)を取り出す
2to_f の呼び出し専用の置き場(フレーム)を作る
3その中で、引数の名前 c の札を 30 に付ける
4本体 return c * 1.8 + 32 を実行する

つまり、引数を渡すことは、`c = x` という代入と同じです。第4章で「代入は札を付けるだけで、コピーはしない」と見ました。引数もまったく同じルールで動いています。

この渡し方は「参照渡し」でも「値渡し」でもなく、Python の公式の FAQ では「代入による受け渡し」(引数は代入で渡される)と説明されています。言葉は覚えなくてかまいません。札の絵が描ければ十分です。

  • JS から来た人の Python 05 ― 関数
  • 4 / 26
  • 呼び出すと、関数の中の名前の置き場ができる
  • グローバル(ファイル全体の名前)
  • オブジェクト
  • to_f
  • to_f
  • function(関数)
  • x
  • 30
  • int
  • to_f の中(呼ぶたびに作られる)
  • 同じ物に付ける
  • c
  • y = to_f(x) # 引数を渡す=関数の中の名前 c を、x と同じオブジェクトに付ける
  • ひらめき
  • to_f(x) で、呼び出し専用の置き場(フレーム)ができ、引数 c の札が同じ 30 に付く。

return で値を返し、置き場は片付けられる

本体の return c * 1.8 + 32 が実行されると、計算結果の 86.0 という float のオブジェクトが新しく作られ、それが呼んだ側に返されます。呼んだ側の y = to_f(x) は、その戻り値に札 y を付けます。

同時に、to_f の呼び出しのために作った置き場(フレーム)は片付けられます。中にあった札 c も一緒に消えます。図から黄色の囲みが消えたのはそのためです。

関数を呼ぶ前呼んでいる間呼び終わった後
x303030
c無い30無い(NameError)
y無い無い86.0

c は関数の中だけで使えるローカルな名前です。関数の外で print(c) とすると NameError: name 'c' is not defined になります。JS の関数の中で let した変数が外から見えないのと同じです。

同じ関数を10回呼べば、置き場も10回作られて10回片付けられます。呼び出しごとに別々の置き場なので、1回目の c と2回目の c が混ざることはありません。

この「呼ぶたびに新しい置き場ができる」は、後で見るデフォルト値の罠を理解する鍵になります。置き場は毎回新しいのに、ある物だけは毎回同じ、という話です。

  • JS から来た人の Python 05 ― 関数
  • 5 / 26
  • return で値を返し、置き場は片付けられる
  • グローバル(ファイル全体の名前)
  • オブジェクト
  • to_f
  • to_f
  • function(関数)
  • x
  • 30
  • int
  • y
  • 戻り値に付ける
  • 86.0
  • to_f の中の置き場は、return と同時に片付けられた(c も消えた)
  • float(戻り値)
  • print(y) # 86.0 ※ c は関数の外からは見えない(NameError)
  • ひらめき
  • return の値に y の札が付き、to_f の中の置き場(c の札)は呼び出しが終わると消える。

list を渡すと、中で変えたものが外にも見える

引数を渡すのが「札を付けるだけ」なら、変更できる物(第4章)を渡したときに何が起きるかは、もう予想できます。

python
def add99(lst):
    lst.append(99)

a = [1]
add99(a)
print(a)      # [1, 99]

図のように、グローバルの札 a と、add99 の中の札 lst は、同じ1つの list に付いています。lst.append(99) はその list そのものを変えるので、関数が終わった後に a から見ても 99 が増えています。

一方、関数の中で lst = [0] と付け替えた場合は、a は変わりません。付け替わるのは関数の中の札 lst だけだからです(第4章の b = [0] と同じ)。

関数の中で呼んだ側の list
lst.append(99)、lst[0] = 5 など(物を変える)変わる
lst = [0](札を付け替える)変わらない
数や文字列を渡して n += 1変わらない(変更できない物なので、新しい物に付け替わるだけ)

これは JS で配列やオブジェクトを関数に渡したときと同じ動きです。

Python の関数でよくある作法は、「渡された list を中で変えるなら、関数の名前や docstring でそう分かるようにする」か、「中で変えずに、新しい list を作って return する」かのどちらかにすることです。標準の list.sort()(元を変える、戻り値は None)と sorted()(新しい list を返す)が、ちょうどこの2つの作法の見本になっています。

  • JS から来た人の Python 05 ― 関数
  • 6 / 26
  • list を渡すと、中で変えたものが外にも見える
  • グローバル
  • a
  • [1, 99]
  • add99 の中
  • list(1つだけ)
  • 同じ物
  • lst
  • def add99(lst):
  • lst.append(99)
  • a = [1]
  • add99(a)
  • print(a) # [1, 99]
  • ※ 中で lst = [0] と付け替えても、a は変わらない(付け替えは中の札だけ)。JS の配列を渡したときと同じ動き
  • ひらめき
  • 引数は札を付けるだけで、コピーはしない。変更できる物を中で変えると、呼んだ側からも見える。

デフォルト引数:省略したときの値

引数の後ろに = 値 と書いておくと、呼ぶ側で省略したときにその値が使われます。デフォルト引数(既定値)です。JS の既定値とほぼ同じ書き方です。

python
def fmt(temp, unit="C", digits=1):
    return f"{temp:.{digits}f}{unit}"

{temp:.{digits}f} は、f-string の書式指定(第3章)の中にさらに {digits} を埋め込んで、小数の桁数を引数で変えられるようにしたものです。

図の表の呼び方は、どれも python3 で実際に動かした結果です。

呼び方結果何が入ったか
fmt(21.456)21.5Cunit と digits は既定の値
fmt(21.456, "F")21.5F2番目の位置に "F"
fmt(21.456, digits=0)21C名前で digits だけを指定。unit は既定のまま
fmt(unit="K", temp=3)3.0K全部を名前で。順番は自由

3行目と4行目の digits=0 のように、呼ぶ側で引数の名前を書いて渡すのが、次に見るキーワード引数です。JS には無い仕組みで、これがあるおかげで「2番目は省略して、3番目だけ変えたい」が素直に書けます。

1つ決まりがあります。既定値の無い引数は、既定値のある引数より前に置く必要があります。def f(a=1, b): と書くと SyntaxError: parameter without a default follows parameter with a default になります。省略されたときに、どの位置の値がどの引数か決められなくなるからです。

  • JS から来た人の Python 05 ― 関数
  • 7 / 26
  • デフォルト引数:省略したときの値
  • def fmt(temp, unit="C", digits=1):
  • return f"{temp:.{digits}f}{unit}"
  • 呼び方
  • 結果
  • 何が入ったか
  • fmt(21.456)
  • 21.5C
  • unit と digits は既定の値
  • fmt(21.456, "F")
  • 21.5F
  • 2番目の位置に "F"
  • fmt(21.456, digits=0)
  • 21C
  • 名前で digits だけ指定
  • fmt(unit="K", temp=3)
  • 3.0K
  • 全部名前で。順番は自由
  • JS:function fmt(temp, unit = "C", digits = 1) { … } ← 書き方も意味もほぼ同じ
  • ひらめき
  • 引数に = で既定の値を書いておくと、呼ぶ側で省略できる。JS の既定値と同じ書き方。

キーワード引数:名前を付けて渡す

呼ぶときに 名前=値 の形で渡す引数をキーワード引数と呼びます。JS から来た人にとって、Python の関数でいちばん便利に感じるところかもしれません。

JS で「引数が多い関数」を作るとき、よく設定のオブジェクトを1つ渡し、受け取る側で分割代入します(図の左)。

js
function fmt(temp, { unit = "C", digits = 1 } = {}) { … }
fmt(21.456, { digits: 0 });

これは、JS に「引数を名前で渡す」仕組みが無いための工夫です。Python は言語の仕組みとしてそれを持っているので、ふつうに引数を並べておけば、呼ぶ側で名前を付けて渡せます(図の右)。

python
def fmt(temp, unit="C", digits=1): …
fmt(21.456, digits=0)
位置引数キーワード引数
渡し方fmt(21.456, "F")fmt(21.456, unit="F")
何で決まるか並んでいる順番名前
良いところ短い読んで意味が分かる。順番を気にしなくてよい

第3章の print("東京", 31, sep=" / ", end="!\n") の sep= と end= も、このキーワード引数です。

JS でも「真偽値の引数は読みにくい」(save(true, false) では何のことか分からない)と言われますが、Python ではそういう引数は save(overwrite=True, backup=False) のように名前で渡すのが作法です。後で見るように、名前でしか渡せない引数を作ることもできます。

  • JS から来た人の Python 05 ― 関数
  • 8 / 26
  • キーワード引数:名前を付けて渡す
  • JavaScript(Node.js)
  • Python
  • function fmt(temp, { unit = "C",
  • def fmt(temp, unit="C", digits=1):
  • digits = 1 } = {}) {
  • …
  • …
  • }
  • fmt(21.456, { digits: 0 });
  • fmt(21.456, digits=0)
  • JS には「名前で渡す」仕組みが無いので、オブジェクトを1つ渡して分割代入で受け取るのが定番
  • Python は言語の仕組みとして持っている。print(…, sep=" / ", end="!") もこれ(第3章)
  • ひらめき
  • JS なら「設定のオブジェクト」を1つ渡すところを、Python は引数の名前で直接渡せる。

*args:残りの位置引数を tuple にまとめる

引数の名前の前に * を付けると、いくつ渡されても全部まとめて受け取れる引数になります。

python
def avg(*temps):
    return sum(temps) / len(temps)

avg(28, 31, 33)       # temps は (28, 31, 33)

受け取った temps は tuple です(公式のチュートリアルにも「tuple に包まれる」とあります)。JS の残余引数 function avg(...temps) とまったく同じ役割で、JS では配列になるところが、Python では tuple になります。

逆に、呼ぶ側で list や tuple の前に * を付けると、中身を広げて1つずつの引数として渡せます。

python
nums = [28, 31]
avg(*nums)            # avg(28, 31) と同じ
場所JSPython意味
受け取る側function f(...rest)def f(*args)余った位置引数をまとめる
渡す側f(...arr)f(*lst)list を広げる

JS の ... が「まとめる」と「広げる」の両方に使われるのと同じく、Python の * も両方に使われます。

名前は *args(arguments の略)がよく使われますが、これはただの慣習です。*temps のように意味の分かる名前にしてかまいません。大事なのは * の記号のほうです。

  • JS から来た人の Python 05 ― 関数
  • 9 / 26
  • *args:残りの位置引数を tuple にまとめる
  • JavaScript(Node.js)
  • Python
  • function avg(...temps) {
  • def avg(*temps):
  • // temps は配列
  • # temps は tuple (28, 31, 33)
  • }
  • return sum(temps) / len(temps)
  • avg(28, 31, 33);
  • avg(28, 31, 33)
  • avg(...nums); // 配列を広げて渡す
  • avg(*nums) # list を広げて渡す
  • 受け取る側の * は「まとめる」、渡す側の * は「広げる」。JS の ... が両方の意味を持つのと同じ
  • 名前の args は慣習で、*temps のように何でもよい。大事なのは * の記号のほう
  • ひらめき
  • 引数の前に * を付けると、いくつ渡されても tuple で受け取れる。JS の残余引数 ...rest と同じ。

**kwargs:残りのキーワード引数を dict にまとめる

* が位置引数をまとめるのに対し、** はキーワード引数をまとめて dict で受け取ります。

python
def report(city, **opts):
    print(city, opts)

report("東京", unit="F", lang="ja")
# 東京 {'unit': 'F', 'lang': 'ja'}

city という名前の引数に当てはまらなかった unit="F" と lang="ja" が、opts という dict に入ります。名前は **kwargs(keyword arguments)が慣習です。

呼ぶ側で dict の前に ** を付けると、中身を広げてキーワード引数として渡せます。

python
o = {"unit": "F"}
report("東京", **o)     # report("東京", unit="F") と同じ

JS と並べると、図の表のようになります。

PythonJS でいうと
**opts で受ける分割代入 { a, ...rest } の rest
**o で渡すオブジェクトのスプレッド { ...o }
*args で受ける残余引数 (...rest)
*nums で渡すスプレッド f(...nums)

第4章の dict のコピー {**d} も、この「広げる」の ** です。

図の右下のとおり、いつも使っている print も、実はこの仕組みでできています。help(print) で説明を表示すると、print(*args, sep=' ', end='\n', file=None, flush=False) と出てきます。*args で表示したい値をいくつでも受け取り、sep や end はキーワード引数で受け取っているわけです。

  • JS から来た人の Python 05 ― 関数
  • 10 / 26
  • **kwargs:残りのキーワード引数を dict にまとめる
  • def report(city, **opts):
  • JS でいうと
  • print(city, opts)
  • **opts で受ける
  • { a, ...rest } の rest
  • report("東京", unit="F", lang="ja")
  • # 東京 {'unit': 'F', 'lang': 'ja'}
  • **o で渡す
  • { ...o } で広げる
  • o = {"unit": "F"}
  • *args で受ける
  • (...rest) の rest
  • report("東京", **o) # dict を広げて渡す
  • # 東京 {'unit': 'F'}
  • *nums で渡す
  • f(...nums)
  • print の正体もこの形:
  • print(*args, sep=' ', end='\n', …)
  • ひらめき
  • ** を付けると、名前付きで渡された残りを dict で受け取れる。渡す側の ** は dict を広げる。

引数の並びには決まった順番がある

ここまでの種類を全部使うと、引数の並びは次の順番になります。

python
def f(a, b=1, *args, c, d=2, **kwargs):
    ...
種類例意味
ふつうの引数a位置でも名前でも渡せる。省略できない
既定値つきb=1省略できる。既定値の無い引数より後ろ
まとめる位置引数*args余った位置引数を tuple で受ける
キーワード専用c、d=2*args より後ろの引数は、名前でしか渡せない
まとめるキーワード引数**kwargs余ったキーワード引数を dict で受ける。必ず最後

*args は要らないけれど「ここから後ろは名前で渡してほしい」ときは、* だけを書きます。

python
def show(day, temp, *, unit="C"):
    ...
show("月", 31, unit="F")    # OK
show("月", 31, "F")         # TypeError: … takes 2 positional arguments but 3 were given

逆に、/ を書くと「ここより前は位置でしか渡せない」になります(Python 3.8 から)。組み込みの関数によく使われていて、help(len) を見ると len(obj, /) と出てきます。

記号意味
/これより前の引数は、位置でしか渡せない
*これより後ろの引数は、名前でしか渡せない

全部を覚える必要はありません。自分で書くときに使うのは、ほとんどが「ふつうの引数」「既定値つき」と、ときどき「* の後ろのキーワード専用」です。他の人のコードや help() の表示で見かけたときに、意味が分かれば十分です。

  • JS から来た人の Python 05 ― 関数
  • 11 / 26
  • 引数の並びには決まった順番がある
  • def f(a, b=1, *args, c, d=2, **kwargs):
  • a
  • b=1
  • *args
  • c, d=2
  • **kwargs
  • 種類
  • 意味
  • a(ふつう)
  • 位置でも名前でも渡せる。省略できない
  • b=1(既定値つき)
  • 省略できる。既定値の無い引数より後ろに置く
  • *args
  • 余った位置引数を tuple で受ける
  • c, d=2(* の後ろ)
  • キーワード専用。必ず c=… と名前で渡す
  • **kwargs
  • 余ったキーワード引数を dict で受ける
  • ひらめき
  • 位置 → 既定値つき → *args → キーワード専用 → **kwargs。* と / で渡し方を縛ることもできる。

罠:デフォルト値に [] を書くと、呼ぶたびに増える

ここで、Python でいちばん有名な罠を見ます。都市の名前を list に足していく関数です。list を渡さなければ、新しい list から始めてほしいので、既定値に [] を書きました。

python
def add_city(city, cities=[]):
    cities.append(city)
    return cities

print(add_city("東京"))
print(add_city("大阪"))
['東京']
['東京', '大阪']

2回目は ['大阪'] になってほしいのに、1回目の「東京」が残っています。3回目を呼べば3つに増えます。

JS で同じことを書くと、期待どおりに動きます。

js
function addCity(city, cities = []) {
  cities.push(city);
  return cities;
}
addCity("東京");   // ['東京']
addCity("大阪");   // ['大阪']

JS の既定値は、呼ぶたびに評価されて、毎回新しい [] ができるからです。

Python の公式チュートリアルは、この動きを「重要な警告」として載せています。「デフォルト値は1回だけしか評価されない。これは、デフォルト値が list や dict のような変更できるオブジェクトのときに違いを生む」。

「1回だけ」とは、いつの1回なのでしょうか。札の図で見てみます。

  • JS から来た人の Python 05 ― 関数
  • 12 / 26
  • 罠:デフォルト値に [] を書くと、呼ぶたびに増える
  • def add_city(city, cities=[]):
  • $ python3 trap.py
  • cities.append(city)
  • ['東京']
  • return cities
  • ['東京', '大阪'] ← ?
  • print(add_city("東京"))
  • print(add_city("大阪"))
  • 注意
  • 期待は ['大阪']
  • 毎回、空の list から始まると思っていた
  • JS で同じことを書くと、呼ぶたびに新しい [] ができるので ['大阪'] になる
  • Python の公式チュートリアルにも「重要な警告」として載っている有名な罠。札の絵で見ると理由が分かる
  • ひらめき
  • cities=[] と書いた関数を2回呼ぶと、2回目に1回目の都市が残っている。なぜ?

デフォルト値は、def を実行したときに1回だけ作られる

前に見たとおり、def の行を実行すると、関数のオブジェクトが1つ作られます。

このとき Python は、既定値の式 [] もその場で1回だけ評価して、できた list を関数のオブジェクトの中にしまっておきます。図の黄色の面が関数のオブジェクト、その中の白い面が「デフォルト値の置き場」です。そこから、def のときに作られた list へ矢印が伸びています。

1回目の呼び出し add_city("東京") では、cities を渡していないので、札 cities は「しまってあった list」に付きます。呼び出しの置き場(フレーム)は毎回新しく作られますが、札が付く先の list は、関数のオブジェクトにしまわれているあの1つです。

そこに append("東京") するので、しまってあった list そのものが ["東京"] に変わります。

いつ起きること
def の行を実行したとき[] を評価して list を1つ作り、関数のオブジェクトにしまう
1回目の呼び出し札 cities を、しまってある list に付ける。"東京" を append
1回目が終わるフレームと札 cities は消える。しまってある list は残る(関数のオブジェクトが持っているから)
  • JS から来た人の Python 05 ― 関数
  • 13 / 26
  • デフォルト値は、def を実行したときに1回だけ作られる
  • add_city(関数のオブジェクト)
  • デフォルト値の置き場
  • ["東京"]
  • cities の既定値 →
  • list(def のときに1つだけ作られた)
  • 1回目:add_city("東京") の中
  • 既定値の list に付ける
  • cities
  • cities を渡さなかったので、札 cities は「しまってあった list」に付く。そこに "東京" を append
  • ひらめき
  • [] が評価されるのは def の行を実行した1回だけ。その list は関数のオブジェクトにしまわれる。

2回目も、同じ list に札が付く

2回目の add_city("大阪") でも、cities を渡していないので、札 cities はまた同じ list に付きます。既定値は作り直されません。1回目で ["東京"] になったその list に "大阪" が足されて、["東京", "大阪"] になります。

しまわれている既定値は、実際に見ることができます。

python
print(add_city.__defaults__)
# (['東京', '大阪'],)

__defaults__ は、関数のオブジェクトが持っている「既定値の置き場」そのものです(引数が複数あれば既定値も複数並ぶので、tuple になっています)。関数もオブジェクトだ、という話は後でもう一度出てきます。

なぜ Python はこういう作りなのでしょうか。既定値の式を呼ぶたびに評価するなら、呼ぶたびに式を実行するぶん遅くなります。Python は「def のときに1回だけ計算しておく」ほうを選びました。既定値が数や文字列や None なら、変更できない物なので、1つを使い回しても何の問題もありません(第4章)。問題になるのは、変更できる物を既定値にしたときだけです。

  • JS から来た人の Python 05 ― 関数
  • 14 / 26
  • 2回目も、同じ list に札が付く
  • add_city(関数のオブジェクト)
  • デフォルト値の置き場
  • ["東京", "大阪"]
  • cities の既定値 →
  • list(def のときに1つだけ作られた)
  • 2回目:add_city("大阪") の中
  • また同じ list
  • cities
  • print(add_city.__defaults__) # (['東京', '大阪'],) ← 関数のオブジェクトにしまわれた list が見える
  • ひらめき
  • 2回目の呼び出しでも、既定値は作り直されない。同じ list に "大阪" が足される。

直し方:None を置いて、中で作る

直し方は決まっています。既定値には変更できない None を置き、関数の中で「None だったら新しい list を作る」と書きます。公式のチュートリアルにも、この形がそのまま載っています。

python
def add_city(city, cities=None):
    if cities is None:
        cities = []          # 呼ぶたびに新しく作る
    cities.append(city)
    return cities

print(add_city("東京"))   # ['東京']
print(add_city("大阪"))   # ['大阪']

cities = [] の行は関数の本体にあるので、呼ぶたびに実行され、毎回新しい list ができます。None の確認に is を使うのは、第3章で見た作法どおりです。

既定値に書くもの安全か理由
None、数、文字列、True安全変更できない物は、共有されても書き換えられない
tuple安全同上
[]、{}、set()危ない変更できる物が、全部の呼び出しで共有される
自分で作ったクラスのインスタンス(第7章)たいてい危ない多くは変更できる物

この罠はとても有名なので、第15章で紹介する ruff などのリンター(コードの検査の道具)には、既定値の [] や {} を見つける規則(ruff では B006)が用意されています。

  • JS から来た人の Python 05 ― 関数
  • 15 / 26
  • 直し方:None を置いて、中で作る
  • def add_city(city, cities=None):
  • 既定値に
  • 安全か
  • if cities is None:
  • cities = [] # 呼ぶたびに新しく作る
  • None・数・文字列
  • 安全(変更できない)
  • cities.append(city)
  • return cities
  • tuple
  • 安全(変更できない)
  • print(add_city("東京")) # ['東京']
  • []・{}・set()
  • 危ない(呼ぶたびに共有)
  • print(add_city("大阪")) # ['大阪']
  • 変更できない物なら、共有されていても
  • 誰にも書き換えられない(第4章)
  • ひらめき
  • 既定値には変更できない None を置き、関数の中で毎回新しい list を作る。これが定番の形。

lambda:式1つだけの小さな関数

JS のアロー関数 (c) => c * 1.8 + 32 にあたるのが、Python の lambda(ラムダ)です。

python
lambda c: c * 1.8 + 32

lambda 引数: 式 と書き、式の値がそのまま返されます。return は書きません。

JS のアロー関数との大きな違いは、中に書けるのは式1つだけということです。公式ドキュメントにも「構文上、1つの式に限られる」とあります。{ } で囲んで何行も書くことはできません。

JSPython
(c) => c * 1.8 + 32lambda c: c * 1.8 + 32
(a, b) => a + blambda a, b: a + b
() => { 文; 文; return x; }書けない。def を使う

これは「複雑な処理には名前を付けて def で書いてほしい」という Python の考え方の表れです。JS ではアロー関数を何でも使いますが、Python で lambda を使うのは主に、関数を引数として渡す場面の、ごく短い処理です。

いちばんよく使うのは、並べ替えの key= です。

python
forecast = [{"day": "月", "temp": 31}, {"day": "火", "temp": 27}]
sorted(forecast, key=lambda f: f["temp"])
# [{'day': '火', 'temp': 27}, {'day': '月', 'temp': 31}]

key= には「要素を1つ受け取って、並べ替えの基準になる値を返す関数」を渡します。JS の arr.sort((a, b) => a.temp - b.temp) のように2つを比べる関数ではなく、1つから基準の値を取り出す関数を渡すところが違います。max(forecast, key=lambda f: f["temp"]) で、いちばん暑い日も取り出せます。

  • JS から来た人の Python 05 ― 関数
  • 16 / 26
  • lambda:式1つだけの小さな関数
  • JS
  • Python
  • (c) => c * 1.8 + 32
  • lambda c: c * 1.8 + 32
  • (a, b) => a + b
  • lambda a, b: a + b
  • () => { 文; 文; return x; }
  • (書けない。def を使う)
  • forecast = [{"day": "月", "temp": 31}, {"day": "火", "temp": 27}]
  • sorted(forecast, key=lambda f: f["temp"]) # 気温の低い順
  • # [{'day': '火', 'temp': 27}, {'day': '月', 'temp': 31}]
  • ひらめき
  • lambda 引数: 式。JS のアロー関数に近いが、中に書けるのは式1つだけ。並べ替えの key によく使う。

名前は L・E・G・B の順に探される

関数の中で名前を使ったとき、Python はその名前をどこから探すのでしょうか。公式のチュートリアルは、こう説明しています。「変数を参照すると、まずローカルの記号表を見て、次に外側の関数のローカルの記号表、次にグローバルの記号表、最後に組み込みの名前の表を見る」。

この4つの頭文字を取って、LEGB と呼ばれます。図の入れ子の囲みが、それぞれの範囲です。

頭文字範囲図では
Localいま実行している関数の中黄:inner の中
Enclosingその関数を囲んでいる外側の関数の中青:outer の中
Globalそのファイル(モジュール)全体緑:このファイル
Built-inPython に最初から入っている名前白:組み込み

図の右のコードでは、inner の中で print(x) をしています。x という名前は、inner の中では作られていません。赤い小包の位置、つまりいちばん内側の L から探し始めます。

JS にも、内側から外側へ名前を探しに行く仕組み(スコープチェーン)があり、考え方は同じです。違いは、JS は { } のブロックごとに範囲ができるのに対して、Python は関数ごとにしか範囲ができないことです(後で見ます)。

  • JS から来た人の Python 05 ― 関数
  • 17 / 26
  • 名前は L・E・G・B の順に探される
  • B:組み込み
  • print・len など
  • x = "global"
  • G:このファイル
  • x = "global"
  • def outer():
  • x = "enclosing"
  • E:外側の関数 outer
  • x = "enclosing"
  • def inner():
  • print(x)
  • L:inner の中
  • inner()
  • outer()
  • # → enclosing
  • x はどこ?
  • inner の中には x が無い
  • ひらめき
  • 関数の中で名前を使うと、まず自分の中(L)、次に外側の関数(E)、ファイル(G)、組み込み(B)の順。

内側から外へ。最初に見つかったものを使う

inner の中(L)に x が無いので、Python は1つ外側、outer の中(E)を探します。そこには x = "enclosing" があるので、そこで探すのをやめて、その値を使います。だから、結果は enclosing です。

グローバル(G)にも x = "global" がありますが、そこまでは探しに行きません。内側から探して、最初に見つかったものが使われます。

探す順その範囲に x はあるか結果
1. L(inner の中)無い次へ
2. E(outer の中)あるこれを使う
3. G(ファイル)ある見に行かない
4. B(組み込み)―見に行かない

もし outer の中の x = "enclosing" を消せば、E にも無いので G まで行き、global と表示されます。4つのどこにも無ければ、NameError: name 'x' is not defined です。

内側の関数が、外側の関数の名前を覚えていて使えることを、JS と同じくクロージャと呼びます。outer の呼び出しが終わった後でも、inner が生きていれば、inner は outer の x を見続けられます。

  • JS から来た人の Python 05 ― 関数
  • 18 / 26
  • 内側から外へ。最初に見つかったものを使う
  • B:組み込み
  • print・len など
  • x = "global"
  • G:このファイル
  • x = "global"
  • def outer():
  • x = "enclosing"
  • E:外側の関数 outer
  • x を見つけた
  • x = "enclosing"
  • def inner():
  • print(x)
  • L:inner の中
  • inner()
  • outer()
  • # → enclosing
  • L に無い → E を見る → あった
  • ひらめき
  • inner の中に x が無いので、外側の outer を見る。そこで見つかったので、G までは探しに行かない。

print や len は、いちばん外の組み込み(B)にある

では、print や len や int は、どこにあるのでしょうか。どの関数の中にも、どのファイルにも def print とは書いていません。

これらは、いちばん外側の B(組み込み、built-in) にある名前です。図のように、L・E・G のどこにも len が無いので、最後に B まで探しに行って見つかります。

組み込みの名前は、Python が起動したときに最初から用意されていて、どのファイルからも import せずに使えます。実体は builtins という名前の特別なモジュールで、中身は次のように確かめられます。

python
import builtins
print(len(dir(builtins)))   # 159(3.13 で確かめた数。版によって増減する)

print、len、int、str、list、dict、range、sorted、sum、type、isinstance、True、None や、TypeError などの例外の名前も、すべてここにあります。JS でいえば、console や Math や parseInt のような、どこでも使えるグローバルな名前にあたります。

1つ注意があります。LEGB は内側が優先なので、自分で list = [1, 2] や sum = 0 のように組み込みと同じ名前に代入すると、組み込みが隠れてしまいます。その後で list("abc") と書くと、G の list(ただの list のオブジェクト)が見つかってしまい、TypeError: 'list' object is not callable になります。変数に list・dict・str・sum・id・type などの名前を付けないようにしましょう。

  • JS から来た人の Python 05 ― 関数
  • 19 / 26
  • print や len は、いちばん外の組み込み(B)にある
  • B:組み込み
  • len を見つけた
  • print・len など
  • x = "global"
  • G:このファイル
  • x = "global"
  • def outer():
  • x = "enclosing"
  • E:外側の関数 outer
  • x = "enclosing"
  • def inner():
  • print(x)
  • L:inner の中
  • L→E→G→B
  • inner()
  • outer()
  • # → enclosing
  • どこにも len が無ければ最後は B(組み込み)
  • ひらめき
  • print・len・int などは、どのファイルからも見える「組み込み」の名前。import せずに使える理由。

関数の中で代入した名前は、その関数のローカルになる

LEGB の探し方には、1つ大事な例外があります。関数の中で代入している名前は、その関数全体でローカル(L)扱いになるのです。

python
count = 0
def bump():
    count += 1
bump()
# UnboundLocalError: cannot access local variable 'count'
#   where it is not associated with a value

count += 1 は count = count + 1 なので、bump の中に count への代入があります。Python は def bump を読んだ時点で「count は bump のローカルな名前だ」と決めます。そのため、右辺の count を読むとき、グローバルの 0 ではなく、まだ札の付いていないローカルの `count` を読もうとして、エラーになります。

公式のチュートリアルも「関数の中の代入は、すべてローカルの記号表に値をしまう」と書いています。JS では let や const を書いた場所で範囲が決まりますが、Python には宣言の言葉が無いので、「代入があるかどうか」で決めているのです。

外のグローバルな名前に代入したいときは、global と宣言します。

python
count = 0
def bump():
    global count
    count += 1
bump()
print(count)    # 1

ただし、global は最後の手段です。関数の外の状態を関数の中から書き換えると、どこで値が変わったのか追いにくくなります。ふつうは、必要な値は引数で受け取り、結果は return で返すようにします。状態をまとめて持ちたいなら、第7章のクラスを使います。

  • JS から来た人の Python 05 ― 関数
  • 20 / 26
  • 関数の中で代入した名前は、その関数のローカルになる
  • count = 0
  • count = 0
  • def bump():
  • def bump():
  • count += 1 # 代入がある
  • global count # 外の count を使う
  • bump()
  • count += 1
  • UnboundLocalError: cannot access
  • bump()
  • local variable 'count' where it is
  • print(count) # 1
  • not associated with a value
  • Python は def を読んだ時点で「中で代入している名前はローカル」と決める。だから読む前に書こうとして失敗する
  • global は最後の手段。ふつうは値を引数で渡し、return で返すほうが分かりやすい
  • ひらめき
  • 代入があると、その名前は関数全体でローカル扱い。外の名前を書き換えたいなら global と宣言する。

if や for は、名前の範囲(スコープ)を作らない

JS から来た人がもう1つ驚くのが、これです。

python
for i in range(3):
    last = i

print(i, last)     # 2 2

JS の let や const は { } の中だけで生きるので、for の外で i を使うと ReferenceError になります(ブロックスコープ)。Python には、ブロックスコープがありません。if・for・while・with・try の中で作った名前は、その外でもそのまま使えます。

Python で名前の範囲(スコープ)を作るのは、主に関数・クラス(第7章)・モジュール(ファイル。第8章)です。第4章の内包表記の中の名前も、外には漏れません。

JSPython
範囲を作るもの関数、{ } のブロック(let・const)関数、クラス、モジュール
for の変数を外で使うReferenceError最後の値が残っている
if の中で作った変数を外で使うReferenceError使える(if の中が実行されていれば)

第3章の tenki で、if の中で作った feel を if の外の print で使えたのは、このためです。

注意点は、if の中が実行されなかった場合です。そのときは名前が作られていないので、外で使うと NameError になります。if と else の両方で同じ名前に代入するか、if の前に feel = None のように先に作っておくと安全です。

  • JS から来た人の Python 05 ― 関数
  • 21 / 26
  • if や for は、名前の範囲(スコープ)を作らない
  • JavaScript(Node.js)
  • Python
  • for (let i = 0; i < 3; i++) {
  • for i in range(3):
  • const last = i;
  • last = i
  • }
  • console.log(i);
  • print(i, last)
  • // ReferenceError: i is not defined
  • # 2 2(外でもそのまま使える)
  • JS の let / const は { } の中だけで生きる(ブロックスコープ)。Python にはこれが無い
  • 第3章の tenki で、if の中で作った feel を if の外で使えたのはこのため
  • ひらめき
  • スコープを作るのは関数(とクラス・モジュール)だけ。for の中で作った名前は、for の後も残る。

nonlocal:外側の関数の名前を書き換える

内側の関数から、外側の関数の名前を読むのは自由にできました(前に見た E)。では、書き換えるにはどうすればよいでしょうか。

呼ぶたびに1ずつ増える数を返す「カウンター」を作ってみます。JS では、こう書けます。

js
function counter() {
  let n = 0;
  return () => { n += 1; return n; };
}

内側の関数で n += 1 と書けば、外側の n が変わります。let n を書いた場所で、n がどの範囲の名前かが決まっているからです。

Python で同じように n += 1 と書くと、前に見た規則により、n は内側の関数のローカルになってしまい、UnboundLocalError になります。そこで、「この n は外側の関数のものだ」と伝えるのが nonlocal です。

python
def counter():
    n = 0
    def inc():
        nonlocal n
        n += 1
        return n
    return inc

c = counter()
c()
print(c())     # 2
宣言意味
(何も書かない)代入するならローカル、読むだけなら L→E→G→B の順に探す
global xx はグローバル(ファイル全体)の名前
nonlocal xx は外側の関数(E)の名前

nonlocal を使うのは、こうした小さなクロージャを書くときくらいです。状態が大きくなってきたら、第7章のクラスを使うほうが読みやすくなります。

  • JS から来た人の Python 05 ― 関数
  • 22 / 26
  • nonlocal:外側の関数の名前を書き換える
  • JavaScript(Node.js)
  • Python
  • function counter() {
  • def counter():
  • let n = 0;
  • n = 0
  • return () => {
  • def inc():
  • n += 1; // そのまま書ける
  • nonlocal n # 外側の n
  • return n;
  • n += 1
  • };
  • return n
  • }
  • return inc
  • JS では let で宣言した場所が決まっているので、内側から書いても外側の n が変わる(クロージャ)
  • Python は宣言が無いので、代入すると内側のローカルになる。外側だと伝えるのが nonlocal
  • ひらめき
  • 内側の関数から外側の関数の名前を書き換えるには nonlocal と宣言する。読むだけなら要らない。

関数もオブジェクト:札を付け、渡し、返せる

前に見たとおり、def は function 型のオブジェクトを作り、名前の札を付けるだけでした。ということは、関数のオブジェクトに別の札を付けることもできます。

python
f = to_f        # ( ) を付けない=呼ばずに、同じ関数に札を付ける
f(0)            # 32.0
f.__name__      # 'to_f'(オブジェクトが覚えている自分の名前)
type(f)         # <class 'function'>

to_f() と ( ) を付ければ「呼ぶ」、to_f とだけ書けば「関数のオブジェクトそのもの」です。この区別は JS と同じです。

関数がオブジェクトなので、引数として渡すことも、関数から返すこともできます。

python
def apply(fn, x):
    return fn(x)

apply(to_f, 100)    # 212.0
できること例
別の名前を付けるf = to_f
引数で渡すsorted(…, key=lambda …)、apply(to_f, 100)
関数から返すcounter() が inc を返した(nonlocal の例)
属性を持つf.__name__、f.__doc__、f.__defaults__

これは JS の関数とまったく同じ性質で、「関数は第一級オブジェクト」と言われます。

この性質は、第17章でとても大事になります。FastAPI の @app.get("/weather") という書き方(デコレーター)は、実は「関数を受け取って、どこかに登録する関数」です。あなたが書いた関数のオブジェクトをフレームワークに渡して、フレームワークが後で呼んでくれる。その土台が、ここで見た「関数もオブジェクト」です。

  • JS から来た人の Python 05 ― 関数
  • 23 / 26
  • 関数もオブジェクト:札を付け、渡し、返せる
  • to_f
  • to_f
  • JS と同じ
  • f
  • f = to_f
  • function(関数のオブジェクト)
  • 変数に入れられる
  • 引数で渡せる(コールバック)
  • f = to_f # ( ) を付けない=呼ばずに札を付ける
  • 関数から返せる(クロージャ)
  • f(0) # 32.0
  • def apply(fn, x):
  • 属性を持つ(f.__name__)
  • return fn(x)
  • apply(to_f, 100) # 212.0(関数を渡す)
  • 第17章の @app.get は、これを
  • 使った「関数を受け取る関数」
  • ひらめき
  • 関数は function 型のオブジェクト。別の札を付けたり、引数で渡したり、関数から返したりできる。

tenki を関数に分ける

この章の内容で、tenki を関数に分けます。

python
# tenki.py ― 第5章の版
def to_f(c):
    """摂氏を華氏にする"""
    return c * 1.8 + 32

def feel(temp):
    if temp >= 30:
        return "暑い"
    if temp >= 20:
        return "ちょうどいい"
    return "寒い"

def show(day, temp, *, unit="C"):
    t = to_f(temp) if unit == "F" else temp
    print(f"{day}: {t:.0f}{unit} ({feel(temp)})")

def main():
    forecast = [("月", 31), ("火", 27), ("水", 18)]
    for day, temp in forecast:
        show(day, temp)
    show("月", 31, unit="F")

main()
$ python3 tenki.py
月: 31C (暑い)
火: 27C (ちょうどいい)
水: 18C (寒い)
月: 88F (暑い)
関数役割この章の要素
to_f変換docstring、return
feel判定途中の return(条件に合ったらそこで終わる)
show1日分の表示* の後ろのキーワード専用引数 unit
main全体の流れtuple の list を for day, temp in … で分けて受け取る

最後の main() の1行で、全体が動き始めます。def の行は関数を作って札を付けるだけで、呼ぶまでは中身は動かないからです。

この「ファイルの最後で main() を呼ぶ」形は、第8章で if __name__ == "__main__": という1行に変わります。tenki を他のファイルから import したときに、勝手に main() が動かないようにするためです。

  • JS から来た人の Python 05 ― 関数
  • 24 / 26
  • tenki を関数に分ける
  • # tenki.py ― 第5章の版
  • def to_f(c):
  • """摂氏を華氏にする"""
  • return c * 1.8 + 32
  • def feel(temp):
  • if temp >= 30:
  • $ python3 tenki.py
  • return "暑い"
  • 月: 31C (暑い)
  • if temp >= 20:
  • return "ちょうどいい"
  • 火: 27C (ちょうどいい)
  • return "寒い"
  • 水: 18C (寒い)
  • def show(day, temp, *, unit="C"):
  • t = to_f(temp) if unit == "F" else temp
  • 月: 88F (暑い)
  • print(f"{day}: {t:.0f}{unit} ({feel(temp)})")
  • def main():
  • forecast = [("月", 31), ("火", 27), ("水", 18)]
  • for day, temp in forecast:
  • unit は * の後ろなので、必ず unit="F"
  • show(day, temp)
  • と名前で渡す(読んで意味が分かる)
  • show("月", 31, unit="F")
  • ※ 見やすさのため関数の間の空行を省いた
  • main()
  • ひらめき
  • 変換・判定・表示・全体の流れを、それぞれ名前の付いた関数にする。main() が全体を呼ぶ。

よくある間違いと、出るエラー

関数まわりのエラーは、ほとんどが TypeError です。メッセージには関数の名前と引数の名前が入るので、それを見れば何を直せばよいか分かります。どれも python3 3.13 で確かめたものです。

書いたもの出るエラー・結果直し方
f()(引数 a が必要)TypeError: f() missing 1 required positional argument: 'a'引数を渡す
f(1, b=2)(b という引数は無い)TypeError: f() got an unexpected keyword argument 'b'引数の名前を確かめる
def f(a=1, b):SyntaxError: parameter without a default follows parameter with a default既定値つきの引数を後ろに回す
return を書き忘れたNone が返る(エラーは出ない)return を書く
print(f)(( ) を忘れた)<function f at 0x…> と表示されるf() と呼ぶ
関数の中で count += 1UnboundLocalError: cannot access local variable 'count' …引数で受け取って return で返す(最後の手段は global)
def f(x=[])呼ぶたびに前の値が残る(エラーは出ない)x=None にして、中で [] を作る
list = [1, 2] の後に list("abc")TypeError: 'list' object is not callable組み込みと同じ名前を変数に使わない

エラーが出ないのに結果がおかしい、というときは、表の「エラーは出ない」の行を疑ってください。とくに return の書き忘れ(結果が None になる)と、既定値の `[]`(前の結果が残る)がよくある原因です。

  • JS から来た人の Python 05 ― 関数
  • 25 / 26
  • よくある間違いと、出るエラー
  • 書いたもの
  • 出るエラー・結果
  • 直し方
  • f()(引数 a が要る)
  • TypeError: f() missing 1 required positional …
  • 引数を渡す
  • f(1, b=2)(b は無い)
  • TypeError: f() got an unexpected keyword argument 'b'
  • 引数の名前を確かめる
  • def f(a=1, b):
  • SyntaxError: parameter without a default follows …
  • 既定値つきを後ろに
  • return を書き忘れた
  • None が返る(エラーは出ない)
  • return を書く
  • print(f)(( ) を忘れた)
  • <function f at 0x…> と表示される
  • f() と呼ぶ
  • 中で count += 1
  • UnboundLocalError: cannot access local variable …
  • 引数と return にする
  • def f(x=[])
  • 呼ぶたびに前の値が残る
  • x=None にして中で作る
  • ひらめき
  • 引数の数や名前が合わないと TypeError。メッセージに関数名と引数名が出るので、それを見て直す。

まとめ:関数も、引数も、札で考える

この章で見たことをまとめます。

項目Python ではJS との違い
作るdef 名前(引数): と字下げreturn が無ければ None(undefined ではない)
def とは関数のオブジェクトを作って札を付ける巻き上げは無い。上から順に実行
引数を渡す中の名前の札を、同じオブジェクトに付ける同じ(配列を渡すと中で変えられる)
キーワード引数f(21.4, digits=0)JS は設定のオブジェクトで代用
*args / **kwargstuple / dict にまとめる。渡す側では広げるJS の ... にあたる
既定値def のとき1回だけ評価されるJS は呼ぶたびに評価。[] ではなく None を置く
lambda式1つだけアロー関数より制限が強い
スコープL→E→G→B の順に探す。代入するとローカルブロックスコープが無い。global・nonlocal
関数もオブジェクト札を付け、渡し、返せる同じ

第4章と第5章で、「名前は札、代入も引数も札を付けるだけ」という1つの絵で、変数・コピー・関数の引数・既定値の罠までを説明してきました。Python で「なぜかこう動く」に出会ったら、まずこの絵を描いてみてください。

次の第6章では、繰り返しと分岐と例外を見ます。Python の for は、JS の for (let i = 0; …) とは考え方が違い、「反復できるもの」を1つずつ取り出す仕組みです。range・enumerate・zip の正体、try / except と JS の try / catch の違い、そして後片付けを自動でしてくれる with の仕組みまで分解します。

  • JS から来た人の Python 05 ― 関数
  • 26 / 26
  • まとめ:関数も、引数も、札で考える
  • 項目
  • Python では
  • 作る
  • def 名前(引数): と字下げ。return が無ければ None
  • 引数を渡す
  • 関数の中の名前の札を、同じオブジェクトに付けるだけ(コピーしない)
  • 渡し方
  • 位置・キーワード(名前=値)・既定値・*args(tuple)・**kwargs(dict)
  • 既定値の罠
  • 既定値は def のとき1回だけ作られる。[] ではなく None を置く
  • lambda
  • 式1つだけの無名関数。並べ替えの key などに
  • スコープ
  • L→E→G→B の順に探す。代入するとローカル。if・for は範囲を作らない
  • 関数もオブジェクト
  • 札を付け、渡し、返せる。@デコレーターの土台
  • ひらめき
  • 次の第6章:for は何を回しているのか?range・enumerate・zip、そして try/except と with。

0 件のコメント

読み込み中…

権利侵害・不適切な内容の連絡: [email protected]