4️⃣関数と ``*`` その1: *専用* 引数 ============================================================ 使ったことある方〜?🙋‍♂️🙋‍♀️ 関数定義における *専用* 引数 -------------------------------------------------- * (A)キーワード専用引数(``*``) * (B)位置専用引数(``/``) **関数の呼び出しの話から** 始めましょう 関数には2つの「引数」 -------------------------------------------------- .. list-table:: :widths: 25 75 * - 仮引数 - 関数の **定義** で使う引数 * - 実引数 - 関数 **呼び出し** で渡す値 関数に渡す値(実引数)は2通り -------------------------------------------------- * **位置** 引数 * キーワード引数(= **名前付き** 引数) 仮引数は *位置またはキーワード引数* (`Pythonチュートリアル 4.8.3.1. `_) 関数定義(仮引数)の例 -------------------------------------------------- .. code-block:: python >>> def calculate_bmi(height_m, weight_kg): ... return weight_kg / height_m / height_m 呼び出し(実引数)例 -------------------------------------------------- .. code-block:: python >>> calculate_bmi(1.58, 46) 18.426534209261334 >>> # すべてキーワード引数で渡せば、順番を変えられます >>> calculate_bmi(weight_kg=46, height_m=1.58) 18.426534209261334 >>> # 位置引数はキーワード引数より先に来ます >>> calculate_bmi(1.58, weight_kg=46) 18.426534209261334 ※渡したのは同じ値です 関数の呼び出し方 -------------------------------------------------- .. list-table:: :widths: 20 40 40 * - 実引数 - 順序 - 名前 * - 位置引数 - 渡す値の **順序** が重要 - 付ける名前を知らなくても呼び出せる * - 名前付き引数 - 順序が重要でない - 名前付きで値を渡すので **より明確** に呼び出せる 仮引数の別の観点:デフォルト値 ============================================================ .. list-table:: * - デフォルト値 - 仮引数 * - なし - 必須 * - あり - **オプション** `Pythonチュートリアル 4.8.1. `_ 仮引数にデフォルト値を指定 -------------------------------------------------- .. code-block:: python >>> def calculate_bmi(height_m, weight_kg=61.8): ... return weight_kg / height_m / height_m 平均体重(日本の男性 `国民健康・栄養調査 2018年 `_)を指定 必須の仮引数だけを指定して呼び出し -------------------------------------------------- .. code-block:: python >>> calculate_bmi(1.58) # 位置引数で指定 24.75564813331197 >>> calculate_bmi(height_m=1.58) # 名前付きで指定 24.75564813331197 オプションの仮引数も指定した呼び出し例 -------------------------------------------------- .. code-block:: python >>> calculate_bmi(1.58, 46) # オプションの引数に位置引数で値を渡す 18.426534209261334 >>> calculate_bmi(1.58, weight_kg=46) # オプションの引数に名前付きで値を渡す 18.426534209261334 .. doctestを通すための変数定義 >>> weight_diff, time_diff = 0.5, 3 オプションの仮引数が複数あるとき -------------------------------------------------- .. code-block:: python >>> def flow_rate(weight_diff, time_diff, period=1, units_per_kg=1): ... ... >>> flow_rate(weight_diff, time_diff, 3600, 2.2) * 位置引数で値を渡すと、値の意味が分かりづらい Effective Python 第2版 `項目23のコード `_ より .. revealjs-break:: .. code-block:: python >>> flow_rate(weight_diff, time_diff, period=3600, units_per_kg=2.2) * **名前付き** で渡すと **分かりやすい** * デフォルト値を使わない引数だけを指定できる(``units_per_kg=1.5`` のみ指定) 関数に **追加** する引数は **オプション引数** -------------------------------------------------- 『`Effective Python 第2版`_』項目23「キーワード引数にオプションの振る舞いを与える」 * 必須引数として追加すると既存の呼び出しを壊してしまう * (すでに見たように)オプション引数は **名前付き** で渡す * 分かりやすい・ **必要なものだけ** 指定できる 💡仮引数に位置引数で渡すか、名前付きで渡すかを指定できる! ------------------------------------------------------------ 仮引数デフォルトの「位置またはキーワード引数」に代えて * (A)キーワード専用引数(``*``) * (B)位置専用引数(``/``) 4️⃣-🅰️ キーワード専用引数 ============================================================ * **名前付き** 引数で渡すのを **強制** する * 名前付きで渡すので、渡す順番は自由 関数定義でキーワード専用にする -------------------------------------------------- .. code-block:: python :emphasize-lines: 2 >>> # 2つの引数をどちらもキーワード専用とした >>> def calculate_bmi(*, height_m, weight_kg): ... return weight_kg / height_m / height_m ``*`` **以降** の仮引数は、キーワード専用引数となる 名前付き引数でのみ呼び出せる! -------------------------------------------------- .. code-block:: python >>> calculate_bmi(weight_kg=46, height_m=1.58) 18.426534209261334 >>> calculate_bmi(1.58, 46) Traceback (most recent call last): File "", line 1, in TypeError: calculate_bmi() takes 0 positional arguments but 2 were given キーワード専用引数の使い所 -------------------------------------------------- * **関数呼び出しの意図を明確に** するために使う(『`Effective Python 第2版`_』項目25) * IMO: *オプション(=デフォルト値あり)の引数* は、キーワード専用が望ましいのではないか * オプション引数に位置引数で渡すのは分かりにくさを生むので、それをさせない 組み込み関数におけるキーワード専用引数🏃‍♂️ -------------------------------------------------- `max `_ や `min `_ の ``key`` 引数 .. code-block:: python >>> fruits_prices = {"apple": 100, "banana": 50, "melon": 777} >>> min(fruits_prices) # keyが最小(アルファベット順で先頭) 'apple' >>> min(fruits_prices.items()) # keyが最小(アルファベット順で先頭) ('apple', 100) >>> min(fruits_prices.items(), key=lambda pair: pair[1]) ('banana', 50) 4️⃣-🅱️ 位置専用引数 ============================================================ * **位置** 引数で渡すのを **強制** する * 名前付きでは渡せなくなる(**名前を知らずに使って** ほしい) * Python **3.8** から(`PEP 570 `_) 関数定義で位置専用にする -------------------------------------------------- .. code-block:: python :emphasize-lines: 2 >>> # 2つの引数をどちらも位置専用とした >>> def calculate_bmi(height_m, weight_kg, /): ... return weight_kg / height_m / height_m ``/`` **まで** の仮引数は、位置専用引数となる 位置引数でのみ呼び出せる! -------------------------------------------------- .. code-block:: python >>> calculate_bmi(1.58, 46) 18.426534209261334 >>> calculate_bmi(weight_kg=46, height_m=1.58) Traceback (most recent call last): File "", line 1, in TypeError: calculate_bmi() got some positional-only arguments passed as keyword arguments: 'height_m, weight_kg' 位置専用引数の使い所 -------------------------------------------------- * 将来、 **引数名を変更可能** にするために使う * 引数名が有用ではない場合にキーワード引数としての利用を排除する(`What's New In Python 3.8 `__) 組み込み関数における位置専用引数🏃‍♂️ -------------------------------------------------- `sum `_ の第1引数 ``iterable`` .. code-block:: python >>> sum(range(1, 11)) 55 IMO: ``iterable=`` と指定しないほうがスッキリ まとめ🥟:関数と ``*`` その1: 専用引数 ============================================================ .. list-table:: :widths: 10 20 30 40 * - - 仮引数 - 制限 - 利点 * - ``*`` - **キーワード専用** - 位置引数で渡せない - 呼び出し意図が明確。オプション引数と相性よい * - ``/`` - **位置専用** - 名前付き引数で渡せない - 引数名が将来変えられる