見出し画像

pythonプログラム初歩の初歩6/関数と引数

こんにちはmakokonです。まだまだPython初心者ですが、初心者であることを意識している限り、新しいスキルを磨く事ができます。
このブログではPythonの"関数"について解説します。雰囲気で使っていても問題はないかもしれませんが、一歩進んでその本質を理解することで、あなたのPythonスキルは確実にアップグレードされます。一緒に勉強していきましょう。


今日のテーマ Hello World

最初のテーマは、「Hello World」を表示するプログラムです。あまりに馴染みが深いプログラムですが、まず普通に書いてみましょう。

# ex.01
print("Hello World")
# ex.02
print("Hello","World")

Hello World
Hello World

実行結果

例1は、"Hello World"という文字列一つを表示していて、例2では"Hello"と"World"の2つの文字列を並べて表示しています。print()も立派にpythonの関数なのですが、今回は2つの文字列を並べる関数がテーマです。
つまり、2つの文字列を並べて表示する関数を書いて、その文字列のやり取りを確認しようという記事です。

引数

関数にわたす変数のことを引数(ひきすう)といいます。
引数は関数が、処理に必要な情報を「引く」つまり取り組むというような感じです。数は単なる「かず」ではなく、項目とか要素とかいう意味のようです。
つまり、関数が処理に必要な取り込むべき情報を引数としてわたします。
なお、今回のプログラムでは引数をarg1,arg2,arg3,,,,,のように表記します。
argは"argument"(引数)を意味して、1番目の引数、2番目の引数、、、です。

位置引数

# 位置引数
def hello1(arg1, arg2):
    print(f"arg1: {arg1}, arg2: {arg2}")

hello1("Hello", "World")  # arg1: Hello, arg2: World
hello1("World", "Hello")  # 位置を入れ替えてみる

arg1: Hello, arg2: World
arg1: World, arg2: Hello

実行結果

def hello1(arg1,arg2) でhello1の引数は、ただ並べて定義されています。
最初に渡される引数がarg1、2番目に渡される引数がarg2ということですね。だから引数を入れ替えて渡すと、順番が入れ替わって表示されます。
makokonのような、C言語上がりのプログラマはほぼこんな書き方をします(偏見)。

キーワード引数

位置引数は、書きやすいのですが、関数を呼び出すときに必要な情報を渡す順番をきちんと把握して置かなくてはいけません。順番を間違えると意味が変わってしまう理由ですかね。例えば、画像を処理するときに、縦サイズと横サイズが必要でもどっちを先に書くのか間違えたらまず正しく動きません。

# キーワード引数
def hello2(arg1, arg2):
    print(f"arg1: {arg1}, arg2: {arg2}")

hello2(arg1="Hello", arg2="World")  # arg1: Hello, arg2: World
hello2(arg2="World", arg1="Hello")  # arg1: Hello, arg2: World

arg1: Hello, arg2: World
arg1: Hello, arg2: World

実行結果

実は、このhello2と前のhello1は同じ関数ですが、何が違うかと言うと
呼び出すときに、
hello2(arg2="World", arg1="Hello") # arg1: Hello, arg2: World
ように、引数の名前をキーワードとして、渡しています。
arg1="Hello"のように明示してわたすことで、引数の順番が変わっても間違いなく正しい方に引き渡すことができます。
画像関数の例でいうと、func(height=1080,width=1920)のようにかいて渡せれば、高さが1080ピクセル、幅が1920ピクセルなんだなとわかります。つまり、関数を定義する方でもよっぽど汎用性が高くて、何にでも使える基本的な関数でない限り、引数の名前も意味がわかる名前をつけといたほうがいいですね。

デフォルト引数

関数によっては、だいたいいつでも同じ内容を処理していてわざわざ引数として渡すまでもないものが多いです。だけど、決め打ちしてしまうとたまに違うことをしたいときに別の関数を書かないと行けないから悩ましい。
hello()関数だと、だいたい「Hello」といえば「World」と応えるのがお約束みたいなもんだけど、たまには「Hello makokon」みたいに、言ってほしいこともある。

# デフォルト引数
def hello3(arg1, arg2="World"):
    print(f"arg1: {arg1}, arg2: {arg2}")

hello3("Hello")  # arg1: Hello, arg2: World(default)
hello3("Good Morning")  # arg1: Good Morning, arg2: World(default)
hello3("Hello","makokon")  # arg1: Hello, arg2: makokon

arg1: Hello, arg2: World
arg1: Good Morning, arg2: World
arg1: Hello, arg2: makokon

実行結果

def hello3(arg1, arg2="World"): では、題2引数のarg2にデフォルトで"World"が設定されています。そのため題2引数がないときには、自動的に"World"が表示されます。しかし、
hello3("Hello","makokon") のように2つとも指定すれば、arg2もデフォルトでなく、"makokon"になるわけですね。

可変長引数

今までの例では、引数の数が2つと決まっていました。でも、関数の役割によっては、引数がいくつ渡されるかわからないものもあります。
最初に書いたprint()もその例ですね。

# ex.01
print("Hello World")
# ex.02
print("Hello","World")

引数が1個でも2個でも正しく動きます。このような関数が呼び出されるときに、使われるのが、可変長引数と言われるものです。
あんまり初歩の初歩とは言えないので、簡単なコード例と、可変長引数が好んで使われる例を紹介します。

# 可変長引数
def hello4(*args, **kwargs):
    for i, arg in enumerate(args):
        print(f"arg{i+1}: {arg}")
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print("result 1: ",'hello4("Hello", "World", name="makokon")')
hello4("Hello", "World", name="makokon")  # arg1: Hello, arg2: World, name: makokon
print("result 2: ",'hello4("Hello", "World", "makokon") ')
hello4("Hello", "World", "makokon")  # arg1: Hello, arg2: World, arg3: makokon

result 1: hello4("Hello", "World", name="makokon")
arg1: Hello
arg2: World
name: makokon
result 2: hello4("Hello", "World", "makokon")
arg1: Hello
arg2: World
arg3: makokon

実行結果

def hello4(*args, **kwargs):
いきなり難易度が上がりましたが、こういうことみたいです。
*argsは任意の数の位置引数をタプルとして受け取り、
**kwargsは任意の数のキーワード引数を辞書として受け取ります。
だから、
結果1のときは、2個の位置引数と、1個のキーワード引数を
結果2のときは、3個の位置引数と、0個のキーワード引数
を受け取っています。こんな感じの引数構造です。

結果1
args=('Hello', 'World')
kwargs={'name': 'makokon'}
結果2
args=('Hello', 'World', 'makokon')
kwargs={}

引数構造例

可変長引数に拘る必要はないよ

便利は便利なんでしょうけど、大抵の場合はプログラムが見にくくなるだけな気がしますね。実際、例えば、複数の文字列を並べて表示するためだけなら、リストを使って、引数を渡すほうがよっぽど使いやすいと思います。

# リスト表示
def hello5(words):
    result = ' '.join(words)
    print(result)
    

hello5(["Hello","World"]) #文字列2個
hello5(["Hello","makokon","World"]) #文字列3個

Hello World
Hello makokon World

実行結果

hello5は引数をリストとして受け取って、その要素をスペースで繋いで、表示します。リストの要素の数がいくつでも問題なく動作します。
キーワード引数を利用するなら、これも最初から辞書で渡すようにしたほうが、見やすいと思います。
必要がないならあえて複雑に書く必要はないと思います。

可変長引数を使うのにふさわしい処理とは

実際に殆ど可変長引数で書いたことがないので、この例が本当にふさわしいのか自信はないのですが、多分これらは可変長引数でないとうまく書けない処理になると思います。
コードの詳細は、ここでは乗せません(初歩の初歩とは思えないから)

  1. 任意の数の引数を受け取る関数を作りたいとき: print()関数のように、任意の数の引数を受け取って処理する関数を作成するときには、可変長引数を使用します。また、Cプログラマなら、引数の数をチェックしてデフォルト処理やエラー処理をするために
    main(int argc,char **argv) のような書き方をしますが、まさにこのための処理ですね。

  2. 関数にオプションの引数を多数設けたいとき: 関数の利便性を向上させるために、多数のオプションを引数として設けたいときには、可変長引数を使用します。この場合、**kwargsを使用して、キーワード引数を任意の数受け取ることができます。
    これもわかりやすいです。普通の情報を位置引数で、オプションをキーワード引数で渡すとして、たくさん混在すると、関数定義がややこしいですよね。

  3. デコレータを作成するとき: デコレータは引数の数や種類が異なる関数に適用可能であるべきなので、可変長引数を使用します。
    これは、python独自のものですね。デコレーターとは、他の関数を「装飾」する(つまりその振る舞いを変更する)ためのものです。具体的には、デコレータは関数を引数として受け取り、新たな関数を返す関数です。既存の関数を利用するのだけど、状況が変わって、少し振る舞いを調整しなくてはならない状況で利用したり、提供された関数を、自分なりの好みの動作を付け足したりするのに使うのでしょう。
    とくに、既存の関数を機能拡張しようとすると、新しい機能ばかりでなく、既存の関数が受け取る可能性がある引数も全部受け入れる必要がある(互換性のため?)ので、パラメータの数も膨大になる可能性もあり、可変長引数でないと、処理できないような気がします。

いずれにしても、可変長引数は単純で明快な処理よりは、汎用性を追求した複雑な条件分岐を含む処理に適していると感じます。LLMの処理などではそれなりに出番があるんじゃないかと思うので、また別の機会に紹介しますね。

今更だけど、関数とメソッド

引数の話は、以上ですがpythonの教科書的なものとして、メソッドという言葉がたくさん出てきます。サンプルコードを見ても関数なんじゃないのと思っていて、

関数とメソッドの主な違いは、メソッドは特定のオブジェクトやクラスに結びついているのに対し、関数はそれ自体が独立しているという点です。

関数:

  • 独立したコードブロックで、特定の処理を行います。

  • 特定のオブジェクトに依存せずに動作します。

  • print(), len(), sum()などがあります。

メソッド:

  • オブジェクトやクラスに属していて、そのオブジェクトの状態や振る舞いを操作します。

  • クラスのインスタンス(オブジェクト)から呼び出されます。

  • .append(), .sort(), .upper()などがあります。

メソッドがオブジェクトに依存することを簡単なプログラムで示します。

def measure_length(item):
    return len(item)

list_object = [1, 2, 3, 4, 5]
string_object = "Hello, world!"

print("リスト",list_object)
print("文字列",string_object)

try:
    print('print(measure_length(list_object))')
    print(measure_length(list_object))  # リストに対してlen関数を適用
    print('print(measure_length(string_object))')
    print(measure_length(string_object))  # 文字列に対してlen関数を適用
except Exception as e:
    print(f"Error in function: {e}")

try:
    print('print(list_object.append(6))')
    list_object.append(6)  # リストに対してappendメソッドを適用
    print(list_object)
except Exception as e:
    print(f"Error in list method: {e}")

try:
    print("print(string_object.append('!'))")
    string_object.append('!')  # 文字列に対してappendメソッドを適用
    print(string_object)
except Exception as e:
    print(f"Error in string method: {e}")

リスト [1, 2, 3, 4, 5]
文字列 Hello, world!
print(measure_length(list_object))
5
print(measure_length(string_object))
13
print(list_object.append(6))
[1, 2, 3, 4, 5, 6]
print(string_object.append('!'))
Error in string method: 'str' object has no attribute 'append'

実行結果

オブジェクトの長さを返す関数len()は、文字列にもリストにも適用可能ですが、新しい要素を付け加えるメソッドappend()は、リストにはありますが、文字列にはありません。したがって、文字列にappend()を適用しようとするとエラーになります。

まとめ

今回の記事では、Pythonでの関数と引数の使い方を解説しました。
具体的な例として"Hello World"を表示するプログラムを用い、
引数とその4つの種類(位置引数、キーワード引数、デフォルト引数、可変長引数)について詳細に説明しました。
さらに、関数とメソッドの違い、特にメソッドが特定のオブジェクトに属するという特性についても触れました。
これらの理解を深めることで、Pythonプログラムの読み書きがよりスムーズになり、より効率的にコードを書くことができるようになることでしょう。
Pythonの学習は、一歩一歩着実に進めていくことが大切です。この記事があなたのPython学習の一助となれば幸いです。

#Python #Python初心者 #Python関数 #引数 #メソッド #プログラミング学習 #コードエッセンシャルズ #初歩の初歩 #4つの引数

おまけ タイトル画の説明 by GPT-4V


この画像は、発想力と創造性を象徴する、幻想的なイラストです。中央に座っている少年が、眼を閉じて夢見心地で空を見上げている様子が描かれています。彼は金色のトカゲを優しく抱えており、大蛇に包まれ安全に感じているようです。周囲には多くの浮遊する電球があり、それぞれからは明るい光が放たれています。さらに、空中には数学の式や記号が散りばめられ、発想や発見のひらめきを表している可能性があります。背景には緑豊かな植物が見られ、画像全体に暖かいトーンが使用されています。このイラストは、自然と科学、想像力が一体となった、刺激的で思考を促す空間を演出しています。


この記事が気に入ったらサポートをしてみませんか?