Python/関数
関数の基礎知識
[編集]関数とは
[編集]関数(function)は、特定の処理をまとめた再利用可能なコードブロックです。入力として引数(parameters/arguments)を受け取り、処理を実行して、必要に応じて結果を戻り値(return value)として返します。
関数を使用する利点
[編集]- コードの再利用:同じ処理を何度も書く必要がなくなり、保守性が向上
- 抽象化:複雑な処理を関数名で表現でき、コードの可読性が向上
- モジュール化:プログラムを機能単位に分割でき、開発効率が向上
- スコープの管理:変数の有効範囲を制御でき、バグの防止に貢献
関数の基本構文
[編集]Python での関数定義の基本形式は以下の通りです:
def関数名(引数1,引数2,...):# 処理内容return戻り値# 省略可能
基本的な関数の例
[編集]defadd_numbers(a,b):"""2つの数値を受け取り、その和を返す関数"""result=a+breturnresult# 関数の使用例sum=add_numbers(5,3)# sum には 8 が代入される
引数の扱い方
[編集]単一引数の例
[編集]defdouble_number(x):"""数値を2倍にする関数"""result=x*2print(f"{x}を2倍したら{result}")returnresult# 使用例double_number(5)# "5を2倍したら10"と出力
複数引数の例
[編集]defcalculate_rectangle_area(width,height):"""長方形の面積を計算する関数"""area=width*heightprint(f"幅{width}、高さ{height}の長方形の面積は{area}")returnarea# 使用例calculate_rectangle_area(3,4)# "幅3、高さ4の長方形の面積は12"と出力
戻り値の扱い方
[編集]戻り値がある場合
[編集]defmultiply(x,y):"""2つの数の積を返す関数"""returnx*yresult=multiply(6,7)# result には 42 が代入される
戻り値がない場合
[編集]defgreet(name):"""挨拶を出力する関数(戻り値なし)"""print(f"こんにちは、{name}さん!")greet("田中")# "こんにちは、田中さん!"と出力
発展的な内容
[編集]デフォルト引数
[編集]引数にデフォルト値を設定することで、引数を省略して関数を呼び出すことができます。
基本的な使用法
[編集]defgreet(name,greeting="こんにちは"):"""挨拶を返す関数(デフォルト引数付き)"""returnf"{greeting}、{name}さん!"print(greet("田中"))# "こんにちは、田中さん!"print(greet("佐藤","おはよう"))# "おはよう、佐藤さん!"
デフォルト引数の注意点
[編集]可変オブジェクトをデフォルト値として使用する際は注意が必要です:
# 危険な例defadd_item(item,lst=[]):# リストをデフォルト引数として使用lst.append(item)returnlstprint(add_item(1))# [1]print(add_item(2))# [1, 2] 予期せぬ結果# 推奨される方法defadd_item_safe(item,lst=None):iflstisNone:lst=[]lst.append(item)returnlst
可変長引数
[編集]*args の使用
[編集]任意の数の位置引数を受け取る場合に使用します:
defsum_all(*args):"""任意の数の引数の合計を計算する関数"""returnsum(args)print(sum_all(1,2,3))# 6print(sum_all(1,2,3,4,5))# 15
**kwargs の使用
[編集]任意の数のキーワード引数を受け取る場合に使用します:
defprint_info(**kwargs):"""キーワード引数の情報を表示する関数"""forkey,valueinkwargs.items():print(f"{key}: {value}")print_info(name="田中",age=25,city="東京")# 出力:# name: 田中# age: 25# city: 東京
キーワード引数
[編集]基本的な使用法
[編集]defcreate_profile(name,age,city="東京"):returnf"名前: {name}, 年齢: {age}, 居住地: {city}"# 位置引数として渡すprint(create_profile("田中",25))# キーワード引数として渡すprint(create_profile(age=25,name="田中",city="大阪"))
キーワード専用引数
[編集]Python 3では、キーワード専用引数を定義できます:
defprocess_data(data,*,sort=False,reverse=False):""" データを処理する関数 sort, reverse はキーワード引数としてのみ指定可能 """result=data.copy()ifsort:result.sort(reverse=reverse)returnresult# 正しい使用法data=[3,1,4,1,5]print(process_data(data,sort=True))# エラーとなる使用法# print(process_data(data, True)) # TypeError
残余引数
[編集]関数の定義で固定された引数とともに、可変長引数を持つことができます。 ここで、残余引数は、可変長引数の後ろに定義される一連の引数です。 残余引数のために、アスタリスク(*
)を使用します。
defmy_func(a,b,*args):print(a)print(b)forarginargs:print(arg)
これで、my_func()関数は少なくともaとbの2つの引数を取り、残りの引数を可変長引数として扱います。
多値返却風な処理
[編集]Pythonには、厳密な意味での多値返却はありませんが、タプルやリストを返すことで多値返しに近いことができます。
- 多値返し風な処理
defaddsub(x,y):a,b=x+y,x-yreturna,bdefmulquoremdiv(x,y):returnx*y,x//y,x%y,x/ya,s=addsub(13,5)m,q,r,d=mulquoremdiv(19,7)print(f'''\{a=}, {s=}{m=}, {q=}, {r=}, {d=}''')
- 実行結果
a=18, s=8 m=133, q=2, r=5, d=2.7142857142857144
「多値返却風な処理」や「多値返しに近いこと」と歯切れが悪いのは、型アノテーションを
defaddsub(x:int,y:int)->int,int:# Error!a,b=x+y,x-yreturna,bdefmulquoremdiv(x:int,y:int)->int,int,int,float:# Error!returnx*y,x//y,x%y,x/y
としたいのですが、
fromtypingimportTupledefaddsub(x:int,y:int)->Tuple[int,int]:a,b=x+y,x-yreturna,bdefmulquoremdiv(x:int,y:int)->Tuple[int,int,int,float]:returnx*y,x//y,x%y,x/y
としなければならず、タプルを返していることを明確に意識する必要があるからです。
デコレーター
[編集]デコレーター(decorator)は、関数の内容を書き換えずに修飾するための仕組みです。 関数の引数に関数を指定します。既存の関数の前に@{デコレーター名}を付けることで、その関数を修飾することができます。
- 形式
defデコレーター名(デコレーターの引数):defラッパー関数名(*args,**kwargs):# 前処理result=デコレーターの引数(*args,**kwargs)# 後処理returnresultreturnラッパー関数名@デコレーター名def修飾される側の関数(その引数):修飾される側の関数の処理
- コード例
defmy_decorator(fn):def_wrapper(*args,**kwargs):print(f"my_decorator<prologue>:{fn.__name__=}:{args=}, {kwargs=}")result=fn(*args,**kwargs)print(f"my_decorator<epilogue>:{result=}")returnresultreturn_wrapper@my_decoratordefcalc(left,right):returnleft+rightprint(f'''\{calc(3,5)=}{calc(right=8,left=9)=}''')
- 実行結果
my_decorator<prologue>:fn.__name__='calc':args=(3, 5), kwargs={} my_decorator<epilogue>:result=8 my_decorator<prologue>:fn.__name__='calc':args=(), kwargs={'right': 8, 'left': 9} my_decorator<epilogue>:result=17 calc(3, 5)=8 calc(right=8, left=9)=17
デコレターの文法は、以下のような糖衣構文です。
@my_decoratordeffn(...):...# はdeffn(...):...fn=my_decorator(fn)#と同じ
デコレーター・パターンは、オブジェクト指向プログラミングにおいて、同じクラスの他のオブジェクトの動作に影響を与えることなく、個々のオブジェクトに動的に動作を追加することができるデザインパターンです。 デコレーター・パターンは、単一責任原則(Single Responsibility Principle)を遵守するために有用で、独自の関心領域を持つクラス間で機能を分割することができます。 デコレーターの使用は、全く新しいオブジェクトを定義することなく、オブジェクトの動作を拡張することができるため、サブクラス化よりも効率的です。
Pythonのデコレーター構文は、デコレーター・パターンの言語支援と言えます。
関数のドキュメント化
[編集]ドックストリング
[編集]関数の説明文をドックストリングとして記述することで、コードの可読性が向上します:
defcalculate_bmi(weight,height):""" 体重と身長からBMIを計算する関数 Args: weight (float): 体重(kg) height (float): 身長(m) Returns: float: BMI値 Examples: >>> calculate_bmi(60, 1.70) 20.76 """returnweight/(height**2)# ドックストリングの参照help(calculate_bmi)
高度な関数の概念
[編集]ラムダ関数
[編集]一行で書ける簡単な関数を定義するときに使用します:
# 通常の関数defsquare(x):returnx**2# 同じ機能のラムダ関数square_lambda=lambdax:x**2# 使用例numbers=[1,2,3,4,5]squared=list(map(square_lambda,numbers))print(squared)# [1, 4, 9, 16, 25]
組込み関数id
[編集]組込み関数idを使うとオブジェクトのidを得ることができます。 2つの変数同士のidが同じ場合、2つの変数は同じオブジェクトにバインドされています。 大概の型のオブジェクトのidはメモリー上のアドレスですが、整数のようにそうではない型も少数ながらあります。
- idの例
"""idの例"""a=1b=2c=3deff2():""" f2で参照されるa,b,cはグローバル変数 """print(f"f2:{a=}({id(a)=}),{b=}({id(b)=}),{c=}({id(c)=}),")deffunc(a):""" funcで参照されるaは仮引数 funcで参照されるbはローカル変数 funcで参照されるcはグローバル変数 """b=a*2print(f"func:{a=}({id(a)=}),{b=}({id(b)=}),{c=}({id(c)=}),")f2()func(111)print(f"GLOBAL:{a=}({id(a)=}),{b=}({id(b)=}),{c=}({id(c)=}),")
- 実行結果
func:a=111(id(a)=9792128),b=222(id(b)=9795680),c=3(id(c)=9788672), f2:a=1(id(a)=9788608),b=2(id(b)=9788640),c=3(id(c)=9788672), GLOBAL:a=1(id(a)=9788608),b=2(id(b)=9788640),c=3(id(c)=9788672),
戻り値
[編集]関数の戻り値(return value;返り値・返却値とも)は、return 文で返します。
- 関数の戻り値
importinspectdeff(a):""" 引数を戻り値とする関数 """returnaprint(inspect.getsource(f))print(f'''\{f(1)=}{f(3.1415926536)=}{f("string")=}''')defn(a):""" return文のない関数 """passprint(inspect.getsource(n))print(f'''\{n(n)=}{n(3.1415926536)=}{n("string")=}''')defx(a):""" return文に引数のない関数 """returnprint(inspect.getsource(x))print(f'''\{x(n)=}{x(3.1415926536)=}{x("string")=}''')
- 関数は多くのケースで何らかの値を返します。
- これを戻り値とよびます。
- 関数は、return文の引数を戻り値とします。
- return文に出会わず関数定義の終わりに達した場合は、オブジェクトNoneが返ります。
- return文で戻り値を指定しない場合は、オブジェクトNoneが返ります。
- 例
deff(a,b):returna+b
- この関数は引数 a と b を受けとり、足した結果を返します。
return文による途中終了
[編集]return文があると、その行で関数が終了します。
- 例
defwork():print("a")return0print("b")work()work()
- 実行結果
a a
- print("b") が実行されることはありません。
関数のスコープ
[編集]スコープとは変数の有効範囲のことで、大きく分けてグローバルスコープとローカルスコープがあります。
グローバルスコープとローカルスコープ
[編集]# グローバル変数global_var=100defmy_function():# ローカル変数local_var=200print(f"グローバル変数: {global_var}")# グローバル変数にアクセス可能print(f"ローカル変数: {local_var}")my_function()print(global_var)# アクセス可能print(local_var)# エラー:ローカル変数にはアクセス不可
globalキーワード
[編集]関数内でグローバル変数を変更する場合は、global
キーワードを使用します:
counter=0defincrement():globalcountercounter+=1returncounterprint(increment())# 1print(increment())# 2
nonlocal
[編集]nonlocalキーワードは、内包関数から、自分を呼び出した関数のローカル変数にアクセスする仕組みです。 global文と違い、新たな変数を作ることはできません。
- コード例
defouter():f=25definner():# 関数内関数nonlocalff=13return1inner()print(f)outer()
- 実行結果
13
- nonlocal の行をコメントにすると、結果は 「25」 に変わります。
クロージャ
[編集]クロージャ(関数クロージャ)は、外側の変数を格納する関数です。
- コード例
deff(z):def_f():nonlocalzz+=1returnzreturn_fq=f(-2)print(q())print(q())print(q())print(q())
- 実行結果
-1 0 1 2
- _fをクロージャ、fをエンクロージャといいます。
この絵柄に見覚えがある人もいると思います。デコレーターの引数(デコレート対象の関数)はラッパー関数は格納した変数です。