Lesson 8 関数の定義 ― Python基礎文法入門機械学習&ディープラーニング入門(Python編)

Python言語の文法を、コードを書く流れに沿って説明していく連載。前回に続けて、今回は関数の定義方法を説明する。加えて、デフォルト引数やキーワード引数という重要機能、Python言語で特徴的なインデントについても説明する。

» 2019年03月25日 05時00分 公開
[一色政彦デジタルアドバンテージ]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「機械学習&ディープラーニング入門(Python編)」のインデックス

連載目次

ご注意:本記事は、@IT/Deep Insider編集部(デジタルアドバンテージ社)が「deepinsider.jp」というサイトから、内容を改変することなく、そのまま「@IT」へと転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。

 前回は「関数」の利用方法について紹介した。今回は、定義方法を説明する。脚注や図、コードリストの番号は前回からの続き番号としている。

 本連載は、実際にライブラリ「TensorFlow」でディープラーニングのコードを書く流れに沿って、具体的にはLesson 1で掲載した図1-a/b/c/dのサンプルコードの順で、基礎文法が学んでいけるように目次を構成している。関数の定義は3つほど含まれているが、すべてを詳しく説明する必要はないだろう。そこで本稿では、前回Lesson 7で説明した図1-aの初出の「関数を使用するコード」、具体的には

mnist.load_data()

という関数が、どのように定義されているかを見てみる。本稿の最後で、図1-b/dに含まれている「関数の定義例」も示す。

 なお、本稿で示すサンプルコードの実行環境については、Lesson 1を一読してほしい。

 Lesson 1でも示したように、本連載のすべてのサンプルコードは、下記のリンク先で実行もしくは参照できる。


Google Colabで実行する
GitHubでソースコードを見る

Python言語の基礎文法

 前回のLesson 7では「キーワード引数」という用語が何度も登場した。この言語機能は、「関数の定義」における「デフォルト引数」という仕様とも関係しているので、両方の言語機能をセットで理解する必要がある。そこで今回は、まず関数の定義方法を示し、その後でこの2つの言語機能を説明することとする。

関数の定義

 それでは、Lesson 7で「関数の利用方法」として説明したload_data()関数が、どのように定義されているのかを見てみよう。

 ライブラリ「TensorFlow」はオープンソースなので、インターネット上で該当箇所のソースコードを簡単に参照できる。例えばload_data()関数は、図12-1のようになっている(2018年10月時点。TensorFlowは頻繁にアップデートされているので、コード内容は随時変わる可能性がある)。

関数を定義するコード例 図12-1 関数を定義するコード例

 図12-1は少し長いので、一部を省略して短くしたリスト11-1も掲載しておこう。

def load_data(path='mnist.npz'):
  # ……内部で何らかの処理を実行……
  x_train, y_train = ['x_train'], ['y_train']
  x_test, y_test = ['x_test'], ['y_test']
  
  return (x_train, y_train), (x_test, y_test)

リスト11-1 関数を定義するコード例

 ポイントは、図12-1で赤色の枠が書かれているところだ。defキーワードで始まる文で関数を定義し、returnキーワードで始まる文で戻り値を返している。図12-2は、関数定義のポイントと、引数と戻り値の関係を表現したイメージだ(前回の図11-2とほぼ同じ絵だが、return ……の文を追記している)。ただし、return文がない関数もある。そうした関数は画面にドットを表示するなど、処理結果を戻り値として返す代わりに別の形で処理結果を表すために使われる。

関数の定義、のイメージ 図12-2 関数の定義、のイメージ

 def文は、以下の構文になっている。

  • 構文: def <関数名>(<引数は0個〜カンマ区切りで複数>):
  • コード例: def load_data(path='mnist.npz'):

インデントで明示するブロック/スコープ

 まずはdef文の最後にコロン:が付けられている点に注目してほしい。このコロンは、次行以降のインデント(後述)された複数行が関数のコードであることを明示するためのものだ。

 インデントとは、半角スペースで左に余白を作ることで、通常は4つの半角スペースで作る。半角スペースの数は、インデント幅と呼ばれる。

 ちなみに、Lesson 3でも説明したように、半角スペースの有無のようなルールはコーディング規約もしくはスタイルガイドと呼ばれる。スペース2つがルールになっているケースもあり、例えば本連載が対象としているTensorFlowのスタイルガイド「TensorFlow Style Guide」では、インデントは半角スペース2つが推奨されている。

 本連載では、横幅節約のため、2つの半角スペースにしている。ちなみに、Google Colabのインデント幅のデフォルト設定も、同じ半角スペース2つである(4つにしたい場合は、メニューバーの[ツール]−[設定]から開く[設定]ダイアログの[インデント幅(スペース)]欄の数値を2から変えることで変更できる)。

 Google Colabでインデントを作成するには、実際に[スペース]キーを2回押す方法だけでなく、行の先頭など行内で[Tab]キーを押してもよい(後述の【注意】を参照)。

 逆にインデントを解除するには、[Shift]+[Tab]キーを押すとよい。

 ただし、Lesson 3で[Tab]キーのショートカットがあることを示したとおり、

  • 入力中の単語の最後で[Tab]キーを押すと(インデントではなく)オートコンプリート
  • 関数の()の間で[Tab]キーを押すと、ヘルプドキュメント(docstringヘルプ)

が表示される。このように[Tab]キーを押す場所で挙動が変わることに注意してほしい。

【注意】[Tab]キーで入力される文字について

 Google Colabでは、[Tab]キーを押すと、キーが表すタブ文字ではなく、半角スペース2つ(デフォルト設定)が入力される点に注意が必要だ。[Tab]キーの挙動がこのようになっており、逆に言うとタブ文字を入力するのは難しい。

 もちろんインデントに(半角スペースではなく)タブ文字を使うことも可能ではある。しかし、そもそもタブ文字は、環境によってインデント幅の見た目が異なる可能性があり、問題がある。よって極力、タブ文字は使わない方がいい。

 エディターによっては、[Tab]キーで(半角スペースによるインデントではなく)タブ文字が入力されてしまう。そのため、コード内でタブ文字と複数スペース(2つか4つ)が混在してしまう可能性がある(混在するとPython 3ではエラーになってしまう)。そうならないためにも、[Tab]キーを押す方法ではなく、実際に[スペース]キーを2回押す方法で、インデント入力の癖を付けておく方が無難かもしれない。ちなみに筆者は、[スペース]キーを2回押している(多くのコードエディターでは、一度、インデントを作ると、その後は改行のたびに同じインデント幅を維持してくれる。つまり、[スペース]キーを2回押すからといって、作業効率はそこまで悪くならない)。


 Pythonは、インデントによって関数の始まり〜終りの範囲(プログラミング用語でブロックと呼び、関数のブロックはスコープとも呼ばれる)を定義するという特徴がある(図12-3)。インデント幅は、このブロックごと統一する必要がある。

インデントで明示する、ブロック/スコープの開始と終了 図12-3 インデントで明示する、ブロック/スコープの開始と終了

 なお、インデントされた複数行の途中に空行があったとしても、それ以降でインデントがそろっている限りは、「ブロック(ここでは関数のスコープ)は終了」と見なされない。よって、複数行の各行はすき間なく詰める必要はなく、見やすいように、適宜、改行で前後に余白を入れながら、ブロックのコードを書いていくことが可能だ(図12-4)。

インデントを継続できる空行と、末尾によるブロックの終了 図12-4 インデントを継続できる空行と、末尾によるブロックの終了
ブロック内の説明をシンプルにするため、load_data()関数ではなくsome_functionという別の関数の例にした。

 ちなみに、インデントを解除しなくても、コードセルやPython(.py)ファイルの末尾に到達すると、当然、ブロック(ここでは関数のスコープ)もそこで終了する(図12-4)。

 このインデントの仕様は、次回Lesson 9以降の「制御フロー文」や「クラス」でもまったく同じなので、ここで確実に押さえてほしい。さらに、「制御フロー文」の「ブロックとスコープの違い」の節で、ブロックとスコープの違いについてより詳しく説明するので、そちらも後で確認してほしい。

デフォルト引数

 再び、図12-1および図12-2の話に戻すと、load_data()関数の引数はpathである。ここで「おかしい」と気付いたかもしれない。先ほどの図10-3で、この関数は「引数なし」として説明した。しかし実際の定義には、引数pathが存在している。つまり、冒頭でも示した関数の使用例(リスト11-2)では、引数の指定を省略していたのである。

#import tensorflow as tf
#mnist = tf.keras.datasets.mnist
# 以下のコードを動かすためには、上記2行を事前に実行しておく必要がある
#---------------------------------------------------------------------

mnist.load_data()

リスト11-2 関数の呼び出しで引数を省略するコード例

 引数を省略せずに指定するには、リスト11-3のように記述すればよい。

mnist.load_data('mnist.npz')

リスト11-3 関数の呼び出しで引数を省略しないコード例

 なぜ引数を省略できるかというと、前掲のリスト11-1を見ると分かるように、

def load_data(path='mnist.npz'):

という関数の定義になっているからだ。つまり関数定義の段階で、引数path'mnist.npz'*5という文字列値がデフォルトで代入されているためである。

*5 'mnist.npz'は、TensorFlowが提供するファイルであり、言語仕様とは関係がないので説明を割愛する。


 このように、デフォルト値を持った引数のことをデフォルト引数と呼ぶ。デフォルト引数があれば、その引数の指定は省略して呼び出せるのである。

 ちなみに復習として、関数の定義でデフォルト引数ではなく通常の引数にする場合は、

def load_data(path):

と、=以降のデフォルト値部分を記載しないようにすればよい。

 デフォルト引数は、引数の数が多いが、通常は決まった値を渡せばよいというときに特に役立つ。例えば関数が10個の引数を持つとしよう(リスト11-3)。

def some_function(param1, param2=2, param3=3, param4=4, param5=5, param6=6, param7=7, param8=8, param9=9, param10=10):
  return param1 + param2 + param3 + param4 + param5 +param6 + param7 +param8 + param9 + param10

リスト11-3 10個の引数(第2引数以降はデフォルト引数あり)を持つ関数定義の例
=の前後には半角スペースを入れてもよい。デフォルト引数の指定では入れないことが一般的。

 この関数を呼び出す場合、通常であれば、リスト11-4のように、すべての引数を順番に指定しなければならない。

some_function(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

# 55と出力される

リスト11-4 10個の引数をすべて指定して関数を呼び出す例

 しかし、この関数は第2引数以降がすべてデフォルト引数である。各引数に渡す値がデフォルト引数と全く同じ値(=第2引数以降に2345678910を順に渡す場合)であるなら、そのすべてが省略可能だ。実際に、第1引数のみ指定して、残りを省略すると、リスト11-5のようになる。

some_function(1)

# 55と出力される

リスト11-5 デフォルト引数により9個の引数指定を省略して関数を呼び出す例

キーワード引数

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。