Python/関数

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

初歩的な内容[編集]

関数[編集]

いくつかの処理をまとめたものを、プログラミングでは「関数」(かんすう)といいます。数学でいう「関数」とは、意味がちがうので、気をつけてください。

なお英語では、数学の「関数」のことを function (ファンクション)といいます。プログラミング用語の「関数」のことも英語では function といいます。

もとの英語をみれば、どちらにも「数」にあたる英語(number 「ナンバー」など)が無いことに、注目してください。

プログラミングの「関数」では、かならずしも、数(整数や小数などの「数」)をつかう必要は、ありません。


さて、pythonでは、ユーザーが自分で関数をつくることができます。ユーザーによる関数の作成には「def」を用います。「def」とは、おそらく定義を意味する英語 definition の頭3文字です。


コード例
def work() :
    print("a")
    print("b")

work()
work()
実行結果
a
b
a
b

このように、繰り返し作業をするときなど、関数が便利です。

関数の定義の書式は、

def 関数名() :
    処理内容

というふうになります。

関数名のあとに、コロン記号「:」をつけるのを、忘れないようにしてください。

関数のプロックは、インデントが終わるまでです。

たとえば下記の関数work()の場合、

def work() :
    print("a")
    print("b")

print("c")
work()

関数のブロックは、

   print("a")
   print("b")

までです。

print("c")は、関数work()のブロックの外です。

引数のある関数[編集]

(コード例)
def func(a) :
    print("計算するよ")
    b = a * 2
    print(a,"を2倍したら",b)

a = 1
func(a)

a = 5
func(a)

a = 80
func(a)

(実行結果↓)

計算するよ
1 を2倍したら 2
計算するよ
5 を2倍したら 10
計算するよ
80 を2倍したら 160

ある処理を繰返し行いたい場合、関数をつかうことで、記述をへらせます。 関数に作業させる際、関数外で定義された値を、上記のように利用する事ができ、そのような変数を「引数」(ひきすう)といいます。

上記のコードのうち、関数の定義部分は、

def func(a) :
    print("計算するよ")
    b = a * 2
    print(a,"を2倍したら",b)

↑ この範囲が、関数の定義部分です。


引数のある関数の定義の書式は、

def 関数名(引数) :
    処理内容

というふうになります。

「引数」は「ひきすう」と読みます。


引数が2個以上の場合[編集]

def menseki(a,b) :
    print("面積を計算するよ")
    print(a * b)

a = 3
b = 5

menseki(a,b)


結果
面積を計算するよ
15


下記のように、呼び出し時に引数の値を「menseki(3,5)」のように指定することもできます。

def menseki(a,b) :
    print("面積を計算するよ")
    print(a * b)

menseki(3,5)


結果
面積を計算するよ
15


やや難しい内容[編集]

グローバル変数とローカル変数[編集]

"""
グローバル変数とローカル変数の例
"""
a = 1
b = 2
c = 3

def f2():
    """
    f2で参照されるa,b,cはグローバル変数
    """
    print(f"f2:{a=},{b=},{c=},")

def func(a) :
    """
    funcで参照されるaは仮引数
    funcで参照されるbはローカル変数
    funcで参照されるcはグローバル変数
    """
    b = a * 2
    print(f"func:{a=},{b=},{c=},")
    f2()

func(111)
print(f"GLOBAL:{a=},{b=},{c=},")
実行結果
func:a=111,b=222,c=3,
f2:a=1,b=2,c=3,
GLOBAL:a=1,b=2,c=3,
f2
関数から呼び出された関数で、仮引数にもローカル変数にも見つからなかった変数は、グローバル変数から探される。
func
仮引数とグローバル変数の名前が同じ場合、仮引数が参照される。
グローバル変数は仮引数にシャドーイングされる。
ローカル変数とグローバル変数の名前が同じ場合、ローカル変数が参照される。
グローバル変数はローカル変数にシャドーイングされる。
ローカル変数の有効範囲(スコープ)は、関数の終わりまでです。

このようにpythonでは、関数内で数を置きかえる動作は、原則として、関数外の動作に影響を与えません。

組込み関数id[編集]

組込み関数idを使うとオブジェクトのidを得ることができます。 2つの変数同士のidが同じ場合、2つの変数は同じオブジェクトにバインドされています。 大概の型のオブジェクトのidはメモリー上のアドレスですが、整数のようにそうではない型も少数ながらあります。

idの例
"""
idの例
"""
a = 1
b = 2
c = 3

def f2():
    """
    f2で参照されるa,b,cはグローバル変数
    """
    print(f"f2:{a=}({id(a)=}),{b=}({id(b)=}),{c=}({id(c)=}),")

def func(a) :
    """
    funcで参照されるaは仮引数
    funcで参照されるbはローカル変数
    funcで参照されるcはグローバル変数
    """
    b = a * 2
    print(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 文で返します。

関数の戻り値
import inspect

def f(a) :
    """ 引数を戻り値とする関数 """
    return a

print(inspect.getsource(f))
print(f'''\
{f(1)=}
{f(3.1415926536)=}
{f("string")=}
''')

def n(a) :
    """ return文のない関数 """
    pass

print(inspect.getsource(n))
print(f'''\
{n(n)=}
{n(3.1415926536)=}
{n("string")=}
''')

def x(a) :
    """ return文に引数のない関数 """
    return

print(inspect.getsource(x))
print(f'''\
{x(n)=}
{x(3.1415926536)=}
{x("string")=}
''')
関数は多くのケースで何らかの値を返します。
これを戻り値とよびます。
関数は、return文の引数を戻り値とします。
return文に出会わず関数定義の終わりに達した場合は、オブジェクトNoneが返ります。
return文で戻り値を指定しない場合は、オブジェクトNoneが返ります。
def f(a, b) :
    return a + b
この関数は引数 a と b を受けとり、足した結果を返します。
return文による途中終了[編集]

return文があると、その行で関数が終了します。

def work() :
    print("a")
    return 0
    print("b")

work()
work()
実行結果
a
a
print("b") が実行されることはありません。

キーワード引数[編集]

関数の仮引数には名前(識別子)がありますが、仮引数を明示して関数の実引数を渡し呼出すことができます。 このような識別子を明示して呼びされた実引数をキーワード引数と言います。

キーワード引数
def sub(left, right) :
    return left - right

print(f"{sub(3, 4)=}")
print(f"{sub(right=8, left=19)=}")
実行結果
sub(3, 4)=-1 
sub(right=8, left=19)=11
同じ関数を、キーワード引数で呼出すことも、従前の引数形式で呼出すこともできます。
キーワード引数では引数の順序は問わず、仮引数の識別子で参照されます。
上記の例では第一引数と第二引数の順序が入れ替わっています。
#可変長引数関数を使うことで、キーワード引数で呼出されたのか、従前の引数形式で呼出されたかを判別できます。

ディフォルト引数[編集]

関数の引数には、既定値(ディフォルト)を設けることができます。

def add(a=0, b=0, c=0) :
    return a+b+c

print(f"""\
{add()=}
{add(1)=}
{add(2,3)=}
{add(4,5,6)=}
""")
実行結果
add()=0
add(1)=1
add(2,3)=5 
add(4,5,6)=15

可変長引数関数[編集]

関数の引数は可変長にできます。

def add(*args, **kwargs) :
    result = 0
    for i in args :
        result += i
    for _, i in kwargs.items() :
        result += i
    return result

print(f"""\
{add()=}
{add(1)=}
{add(2,3)=}
{add(4,5,6)=}
{add(1,2,3,4,5,6,7,8,9,10)=}
{add(a=1)=}
{add(a=2,x=3)=}
{add(q=4,p=5,z=6)=}
""")
実行結果
add()=0
add(1)=1
add(2,3)=5
add(4,5,6)=15
add(1,2,3,4,5,6,7,8,9,10)=55
add(a=1)=1
add(a=2,x=3)=5
add(q=4,p=5,z=6)=15
この様に、キーワード引数を可変長引数に含めることもできます。

多値返却風な処理[編集]

Pythonには、厳密な意味での多値返却はありませんが、タプルやリストを返すことで多値返しに近いことができます。

多値返し風な処理
def addsub(x, y) :
    a, b = x + y, x - y
    return a, b

def mulquoremdiv(x, y) :
    return x * y, x // y, x % y, x / y

a, 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

「多値返却風な処理」や「多値返しに近いこと」と歯切れが悪いのは、型アノテーション

def addsub(x: int, y: int) -> int, int: # Error!
    a, b = x + y, x - y
    return a, b


def mulquoremdiv(x: int, y: int) -> int, int, int, float: # Error!
    return x * y, x // y, x % y, x / y

としたいのですが、

from typing import Tuple

def addsub(x: int, y: int) -> Tuple[int, int]:
    a, b = x + y, x - y
    return a, b

def mulquoremdiv(x: int, y: int) -> Tuple[int, int, int, float] :
    return x * y, x // y, x % y, x / y

としなければならず、タプルを返していることを明確に意識する必要があるからです。

デコレーター[編集]

デコレーター(decorator)は、関数の内容を書き換えずに修飾するための仕組みです。 関数の引数に関数を指定します。既存の関数の前に@{デコレーター名}を付けることで、その関数を修飾することができます。

形式
def デコレーター名(デコレーターの引数) : 
    def ラッパー関数名(*args, **kwargs) :
        # 前処理
        result = デコレーターの引数(*args, **kwargs)
        # 後処理
        return result
    return ラッパー関数名

@デコレーター名
def 修飾される側の関数(その引数) :
    修飾される側の関数の処理
コード例
def my_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=}")
        return result
    return _wrapper

@my_decorator
def calc(left, right) :
    return left + right

print(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_decorator
def fn(...):
    ...

# は

def fn(...):
    ...
fn = my_decorator(fn)

#と同じ
デコレーター・パターン

デコレーター・パターンは、オブジェクト指向プログラミングにおいて、同じクラスの他のオブジェクトの動作に影響を与えることなく、個々のオブジェクトに動的に動作を追加することができるデザインパターンです。 デコレーター・パターンは、単一責任原則(Single Responsibility Principle)を遵守するために有用で、独自の関心領域を持つクラス間で機能を分割することができます。 デコレーターの使用は、全く新しいオブジェクトを定義することなく、オブジェクトの動作を拡張することができるため、サブクラス化よりも効率的です。

Pythonのデコレーター構文は、デコレーター・パターンの言語支援と言えます。


global 文[編集]

ローカル変数や仮引数にグローバル変数がシャドーイングされた場合、 関数の中でも、

global 変数

と変数宣言することにより、その変数をグローバル変数として取り扱えます。

コード例
a = 10
b = 11

def func(a) :
    global b
    print(f"func:{a=},{b=}")
    b = a * 2
    print(f"func:{a=},{b=}")

func(a)
print(f"GLOBAL:{a=},{b=}")
表示結果
func:a=10,b=11
func:a=10,b=20
GLOBAL:a=10,b=20

C言語には、このような機能(関数内で変数をグローバル宣言する機能)は、ありません。 C++では、グローバル変数の参照に :: を前置することでシャドーイングを回避できます。


関数内で新たなグローバル変数を作る[編集]

コード例
a = 10

def func(a) :
    global b
    # print(f"func:{a=},{b=}") ← この時点では b は未定義
    b = a * 2
    print(f"func:{a=},{b=}")

func(a)
print(f"GLOBAL:{a=},{b=}")
表示結果
func:a=10,b=20
GLOBAL:a=10,b=20

nonlocal文[編集]

nonlocal文は、内包関数から、自分を呼び出した関数のローカル変数にアクセスする仕組みです。 global文と違い、新たな変数を作ることはできません。

コード例
def outer() :
    f = 25
    def inner() :    # 関数内関数
        nonlocal f
        f = 13
        return 1
           
    inner()
    print(f)
    
outer()
実行結果
13
nonlocal の行をコメントにすると、結果は 「25」 に変わります。

クロージャ[編集]

クロージャ(関数クロージャ)は、外側の変数を格納する関数です。

コード例
def f(z) :
    def _f() :
        nonlocal z
        z += 1
        return z
    return _f
    
q = f(-2)
print(q())
print(q())
print(q())
print(q())
実行結果
-1
0
1
2
_fをクロージャ、fをエンクロージャといいます。

この絵柄に見覚えがある人もいると思います。デコレーターの引数(デコレート対象の関数)はラッパー関数は格納した変数です。