スポンサーリンク
Python

【Python入門🔰】関数を作成する2種類の方法と使い方

使い始めると便利でしょうがない「関数」。
使ったことがない人やまだ慣れていない人からすると、ちょっと厄介な存在かもしれません。

Google検索で出てくる先人達のソースコードにも、必ずと言っていいほど関数が使われています。
皆さんの中には「def function():」や「lambda x:」が出てきた瞬間、『うわっ…』と感じてしまう人もいるでしょう。

この記事では、Python上で関数の機能を有する「def」と「lambda」の使い方や注意点などについて説明します。

参照元:Python Documentation|4. More Control Flow Tools > 4.7. Defining Functions

スポンサーリンク

関数の機能は?

村人1
村人1

graph(2, 1) って実行したら、

y=2x+1 のグラフが出力されないかなぁ~

村人A
村人A

ggl_sjst(“渋谷”, “美容院”) って実行したら、

Google検索で「渋谷 美容院」と入力したときの

サジェストを取得してくれないかなぁ~

こんな願いを(自分で)叶えるために用意されている機能のことです。

def function_name(arg1, arg2, …):

関数を定義する

def func_1():
  print("関数実行")

def func_2(arg):
  return arg * arg


# 関数実行
func_1()    # >>> 関数実行
func_2(20)  # >>> 400

def は、関数を定義するために使用するキーワードです。

関数を定義する手順は、

  1. def+半角スペースを記述
  2. 関数の名前を記述
  3. 丸括弧内に引数を記述(引数なしも可)
  4. コロンを記述して改行
  5. インデントを挿入して関数に持たせる処理内容を記述する

作成した関数は、関数名(引数1, 引数2, …) と記述すれば実行されます。

return文による戻り値

関数の処理文に、「return 〇〇」という記述を見たことのある方は多いと思います。

return 〇〇で出力される〇〇のことを「戻り値(または返り値)」といいます。
戻り値は関数から1つだけ出力される値で、関数としての(正式な?)出力値になります。

return文を記述しなくても関数を実行することはできます。
また、return文のない関数を使うプログラムも多々あります。
(例えば、tkinterでは関数の戻り値よりも処理自体が必要な場合が多いため、return文のない関数を使います。)

とはいえ、return文が必要な場面はもちろんありますし、戻り値は重要な役割を担います。

「関数内の処理に必要なデータを引数として渡し、処理結果を戻り値として受け取る。
その戻り値を他の変数に代入したり、他の関数の引数として利用する。」

こういった処理は、戻り値がなければできません。

return文の有無による出力結果の違いを下記に示します。

def func_1():
  a = 2 + 2

def func_2():
  a = 2 + 2
  print(a)

def func_3():
  a = 2 + 2
  return a


# 関数実行
func_1()  # >>> 
func_2()  # >>> 4  ← print()による出力
func_3()  # >>> 4  ← 関数としての出力

# print()で出力
print(func_1())  # >>> None
print(func_2())  # >>> 4  None  ← print()による出力と関数としての出力
print(func_3())  # >>> 4  ← 関数としての出力

# 変数に関数を代入
x = func_1()
y = func_2()
z = func_3()
print(x)  # >>> None
print(y)  # >>> None  ← 関数としての出力がないため、代入する値がない
print(z)  # >>> 4  ← 関数としての出力があり、変数に代入されている

引数の設定

関数内の処理に「関数外のデータ」を使用するには、関数定義の際に引数を設定する必要があります。

引数の設定では、渡される引数の順番や引数の渡し方などを指定することができます。

引数にデフォルト値を指定

def func(arg1, arg2=5):
  num = arg1 * arg2
  return num


# arg1を指定して実行
func(2)  # >>> 10

# arg1とarg2を指定して実行
func(2, 10)  # >>> 20

関数を定義する際に、引数に対してデフォルト値を設定することができます。

デフォルト値を設定することで、その引数が渡されない場合でも関数が実行されます。

また、実行時にデフォルト値を持つ引数にデータを代入することも可能です。

よって、入力されないと困る引数については、デフォルト値を設定しておくと良いでしょう。

引数のデフォルト値が変化する可能性

引数のデフォルト値が辞書やリストなどのように変更可能なデータの場合、
設定した値から変化してしまう場合があります。

下記のコードのように、何度も使用する関数を定義するときには、仮実行を行ないデフォルト値が変化してしまわないか確認する作業が重要になってきます。

def func(dic={"one": 1}, **keywords):
  for kwd in keywords:
    dic[kwd] = keywords[kwd]
    print(dic)

# 引数を渡して関数実行
func(two = 2)  # >>> {'one': 1, 'two': 2}
func(three = 3)  # >>> {'one': 1, 'two': 2, 'three': 3}
func(one = 4)  # >>> {'one': 4, 'two': 2, 'three': 3}
func(four = 4)  # >>> {'one': 4, 'two': 2, 'three': 3, 'four': 4}

キーワードによる引数指定

def func(arg1, arg2="おはようございます"):
  print(fr"{arg1}さん、{arg2}!")

name = "渡邉"
greet = "こんにちは"

# arg1を指定して実行
func(arg1=name)  # >>> 渡邉さん、おはようございます!
func(name)  # >>> 渡邉さん、おはようございます!

# arg1とarg2を指定して実行
func(name, arg2=greet)  # >>> 渡邉さん、こんにちは!
func(arg2=greet, arg1=name)  # >>> 渡邉さん、こんにちは!

# 同じ引数を2回指定することはできない
func(name, greet, arg2="こんばんは")
# >>> TypeError: func() got multiple values for argument 'arg2'

keyword=value(キーワード+データ)の形で指定する引数を「キーワード引数」といいます。

キーワード引数のメリットは、引数の順番に縛られないことです。

データだけの引数(位置引数)を渡した場合、関数は渡された順番に引数を処理していきます。
そのため、予期していない順番によるエラーが発生する可能性が高まります。

一方で、キーワード引数を使った場合、引数を渡すときの順番に縛られないため関数定義の時に想定した各引数に対する処理が行われます。

キーワード引数の注意点としては、以下が挙げられます。

  • 位置引数のあとに渡さなければならないこと
  • 関数定義の時に指定したキーワード以外を渡すとエラーになる

def function(parameter, *arguments, **keywords):

上記のような関数定義の記述方法を見たことのある方は多いのではないでしょうか。

ライブラリ内のメソッドについてネットで調べてみると、こういった記述が多く見られます。

「*arguments」や「**keywords」は、引数を渡す際の条件を表しています。

「*arguments」は、引数を”位置(順番)”によって判断しています。
「**keywords」は、引数を”キーワード”によって判断しています。

下記のコードをご覧ください。

def greeting(name, *sentence, **answer):
  print(fr"おぉ、{name}!おはよう!")
  for stc in sentence:
    print(stc)
  print("Q1. どう答える?")
  for ans in answer:
    print(ans, ":", answer[ans])

cvst_1 = "なんか眠そうだね"
cvst_2 = "夜更かしでもした?"

# 引数を渡して関数実行
greeting("渡邉", cvst_1, cvst_2,
         A="ゲームやりすぎた", B="し、してないよ", C="勉強してた")

'''実行結果
おぉ、渡邉!おはよう!
なんか眠そうだね
夜更かしでもした?
Q1. どう答える?
A : ゲームやりすぎた
B : し、してないよ
C : 勉強してた
'''

引数を分けて考えると、

  • name → “渡邉”
  • *sentence → cvst_1, cvst_2
  • **answer → A=”ゲームやりすぎた”, B=”し、してないよ”, C=”勉強してた”

「*sentence」は、渡された引数の順番通りに処理・出力を行なっています。
「**answer」は、キーワードと共に渡された引数を処理・出力しています。

ざっくり説明すると、

「*sentence」は、渡された引数をタプル(丸括弧)にまとめて関数内に取り込んでいる
「**answer」は、キーワード引数をマッピング型(中括弧)で関数内に取り込んでいる

タプルは丸括弧内にデータが入っており、データの順番を変えたりデータを置き換えたりすることができない(不変の)配列です。
マッピング型は、{“name”: “country”, “Bob”: “America”, “Kevin”: “England”} のような型をしたデータを指します。

まとめると、
*arguments:キーワードなしで引数を渡す|取り出し方はタプルと一緒
**keywords:キーワードありで引数を渡す|取り出し方はマッピング型と一緒
ということになります。

引数の条件指定( / と * )

def function(pos1, pos2, /, par1, par2, *, key1, key2):
  pass

関数を定義する際に、引数の中に「/」と「*」を追加することで渡される引数の条件を指定することができます。

「/」よりも前にある引数は「位置専用引数」となり、キーワード引数を渡すとエラーが発生します。
「*」よりも後ろにある引数は「キーワード専用引数」となり、位置引数(データだけ)を渡すとエラーが発生します。
「/」と「*」の間にある または どちらもない場合の引数は、位置引数でもキーワード引数でも渡すことが可能(通常通り)です。

このような条件指定を行なう理由は、

  • 位置専用引数:引数の名前(キーワード)が関数の内容と関係がなく、関数を使う人にとって必要のない情報の場合|引数の順番が重要な場合
  • キーワード専用引数:引数の名前が関数に深く関わっていて、関数を使う人にとってその名前が必要な情報の場合|引数の順番に縛られない方が良い場合

ソースコードを様々な人に使ってもらいたいと考えた際に条件指定を使うことで、ユーザーにとって理解しやすく使いやすいコードになるのではないでしょうか。

lambda [arguments_list] : function

lambda x: x + 1  # >>> <function <lambda> at 0x000...>

func = lambda x: x + 1
func  # >>> <function <lambda> at 0x000...>

func(11)  # >>> 12
func(22)  # >>> 23

ラムダ式は「名前のない小さな関数」と表現されます。

def で定義する関数には名前を付けることができますが、ラムダ式にそのような機能はありません。
また、ラムダ式の性質として、処理文は単一の式だけしか指定できないため複雑な処理を行なうことには向いていません。

しかし、ラムダ式は def のように引数を持たせてそれを使用した処理文を記述することができます。
また、ある変数に「引数を持たせたラムダ式」を代入することで、その変数に関数の機能を持たせることができます。

そして、最も優れた機能が、関数が要求されている場所であればどこでも使用できるという点です。

関数が要求されている場所に記述するのは、基本的に関数名のみです。
引数を渡した状態:関数名(引数1, 引数2) で記述するとエラーまたは該当するメソッドが上手く機能しなくなってしまいます。

そこで使用するのがラムダ式です。

要求場所に
lambda arg1, arg2: function(arg1, arg2)
と記述することで、関数に引数を渡した状態で要求に応じることができます。

ラムダ式は、変数への代入ではなく関数が要求されている場所への使用を推奨します。

利点で「代入できるよ!」と言っておいてすみません^^;

例えば、「引数:x」「式:x+2」の場合、
「f = lambda x: x+2」→「def f(x): return x+2」
とするほうが良いということです。

理由としては、

  • この場合、関数として作成したいのはラムダ式ではなく、「f」ということ。
  • ラムダ式の「関数が要求された場所へ忍び込める」という利点が薄まること。

2つ目はラムダ式への思いが溢れてしまっているので、そこまで気にしなくて大丈夫です。

1つ目が重要ですね。def は「関数定義」を意味します。
関数を作成するという点においてlambda は、def にはない利点を持った補助的な位置づけだと思っています。

ですので、関数を作りたいときは def の方が良いんじゃないかな?と思っている次第です。
すみませんが、使い慣れてきたらで良いので考えてみてください。

ブログ内でラムダ式を使用している記事は以下になります。参考になれば幸いです。

【nfcpy & PaSoRi】PythonでICカードのIDmを読み取ってみよう
私たちが普段利用しているクレジットカードや交通系ICカードなどには、SONYの非接触ICカード技術方式「FeliCa」が採用されています。 また、それらFeliCaカードの情報を読み取る端末として、非接触ICカードリーダー/ライター「PaSoRi(パソリ)」が同じくSONYからリリースされています。 一方で、プログラミング言語「Python」には、PaSoRiなどの非接触型デバイスを動作させるためのライブラリ「nfcpy」が用意されています。 この記事では、nfcpyとPaSoRiを使ってICカードのIDm(ICチップの固有製造ID)を読み取る方法を紹介します。
【tkinter】PythonでYouTubeの動画をダウンロードするGUIアプリ作成
以前、Pythonライブラリのpytubeを利用してYouTubeの動画をダウンロードする方法を紹介しました。上記の方法はコマンドプロンプト上で操作しており、プログラミングに慣れていない人からするととっつきにくいと感じるかもしれません。そこで、今回は動画のダウンロード作業をGUI上で行えるようするため、Tkinterを使ってGUIアプリを作成しました。この記事では、PythonのGUI構築ライブラリ「Tkinter」を利用して、YouTubeの動画をダウンロードするためのGUI作成方法を説明します。

まとめ

この記事では、Python上で関数の機能を有する「def」と「lambda」の使い方や注意点などについて説明しました。

関数は自分が欲しいと思った出力をくれる便利な機能です。
ただし、望んだ出力をくれる関数は自分で作らなければなりません。

使い始めは難しいと感じるかもしれませんが、始めてしまえば慣れるまではあっという間です。
別に関数として作らなくていいんじゃない?という処理についても作っちゃってください!

気付けば適材適所で使えるようになっているはずです。応援しています。

コメント

スポンサーリンク
タイトルとURLをコピーしました