S-JIS[2018-01-08/2018-01-15] 変更履歴

Pythonの関数定義

Python3.6.4の関数(メソッド)のメモ。


概要

関数はdef文で定義する。(関数オブジェクトを生成する)
クラス定義の中でdef文を書くとメソッド定義になる。

def 関数名(〔引数名〔= 初期値〕〔, 引数…〕〕)〔-> 戻り値の型〕: 複文

関数名や引数名はsnake_caseにするのが慣例らしい。

引数名の前に「*」を付けると任意引数リスト(可変長引数)になる。

関数の戻り値はreturn文で返す。
(Scalaだと一番最後の文の値が返り値となるが、Pythonではreturn文を書かないと戻り値なしになる(厳密にはNoneが返る))


作成した関数は基本的に以下のようにして呼び出す。

関数名(値, 値…)

関数呼び出しの例


引数の定義

def文の関数名の後ろに丸括弧を付け、引数名をカンマ区切りで並べる。

def 関数名(〔引数, …〕): 複文

Pythonでは引数(変数)に型は無いので、制限がある場合はドキュメンテーション文字列で記載する。

パターン 関数呼び出しの例 説明
引数0個
def f0():
    print("...")
f0()
 
引数1個
def f1(arg):
    print(arg)
f1("abc")
 
引数2個
def f2(arg1, arg2):
    print(arg1, arg2)
f2("abc", 123)
 
末尾カンマ
def fc(arg1, arg2,):
    print(arg1, arg2)
fc("abc", 123)
関数定義の最後の引数の末尾にカンマ「,」を付けても良い。[2018-01-13]
def fc(arg1, arg2):
    print(arg1, arg2)
fc("abc", 123,)
関数呼び出しの最後の引数の末尾にカンマ「,」を付けても良い。[2018-01-13]
キーワード引数
def f2(arg1, arg2):
    print(arg1, arg2)
f2(arg1 = "abc", arg2 = 123)
f2(arg2 = 123, arg1 = "abc")

f2("abc", arg2 = 123)
引数名を使って呼び出すことが出来る。
デフォルト引数
def f2(arg1 = "zzz", arg2 = 0):
    print(arg1, arg2)
f2()
f2("abc")
f2(arg2 = 123)
引数のデフォルト値を設定しておくと、呼び出し時に省略できる。
タプルを引数にする
def f2(arg1, arg2):
    print(arg1, arg2)
t = ("abc", 123) # タプル
f2(*t)
シーケンス(タプルリスト)の各要素を引数に対応付けて渡すことが出来る。(引数リストのアンパック
関数を呼び出すときにシーケンスの前に「*」を付ける。
def f3(a1, a2, a3):
    print(a1, a2, a3)
t = ("abc", 123) # タプル
f3(1, *t)
f3(*t, 3)
一部分だけをタプルで呼び出すことも出来る。
dictをキーワード引数にする
def f3(a1, a2, a3):
    print(a1, a2, a3)
d = {"a1": 1, "a2": 2, "a3": 3}
f3(**d)
dictの要素(キーと値)を引数名に対応付けて渡すことが出来る。(引数リストのアンパック
関数を呼び出すときにdictの前に「**」を付ける。
def f3(a1, a2, a3):
    print(a1, a2, a3)
d = {"a1": 1, "a3": 3}
f3(**d, a2 = 2)
f3(a2 = 2, **d)
一部分だけをdictで呼び出すことも出来る。
def f3(a1 = 1, a2 = 2, a3 = 3):
    print(a1, a2, a3)
d = {"a1": 11, "a3": 33}
f3(**d)
デフォルト引数あり版。

任意引数リスト

個数が可変の引数(いわゆる可変長引数)も定義できる。
引数名の前に「*」を付ける。
この変数にはタプルで入ってくる。

また、任意のキーワード引数を受け取ることも出来る。[2018-01-13]
一番最後の引数の引数名の前に「**」を付ける。
この変数にはdictで入ってくる。

パターン 関数呼び出しの例 argsの値 説明
可変長引数のみ
def f0(*args):
    print(args)
f0()
f0(1)
f0(1, 2, 3)
()
(1,)
(1, 2, 3)
 
固定の引数と可変長引数
def f2(arg1, arg2, *args):
    print(args)
f2("a", "b")
f2("a", "b", "c")
f2("a", "b", "c", "d")
()
('c',)
('c', 'd')
基本的に、可変長引数は一番最後の引数に指定する。
キーワード引数
def f3(arg1, *args, arg3):
    print(args, arg3)
f3(1, arg3 = 9)
f3(1, 2, arg3 = 9)
f3(1, 2, 3, arg3 = 9)
()
(2,)
(2, 3)
可変長引数の後ろにキーワード引数を定義することは出来る。
キーワード引数にデフォルト値を設定しないと、呼び出し時に必須になる。
キーワード引数
(デフォルト値あり)
def f3(arg1, *args, arg3 = 0):
    print(args, arg3)
f3(1)
f3(1, 2)
f3(1, 2, 3)
f3(1, 2, 3, arg3 = 9)
()
(2,)
(2, 3)
(2, 3)
 
任意のキーワード引数
def fk(arg1, **kwargs):
    print(kwargs)
fk(1)
fk(1, a = 2)
fk(1, a = 2, b = 3)
{}
{'a': 2}
{'a': 2, 'b': 3}
任意のキーワード引数「**」の例。[2018-01-13]
def fk(arg1, *args, **kwargs):
    print(f"{args} {kwargs}")

 

fk(1)
fk(1, 2, 3)
fk(1, a = 4)
fk(1, 2, 3, a = 4)
() {}
(2, 3) {}
() {'a': 4}
(2, 3) {'a': 4}
任意の引数と任意のキーワード引数を混在させる例。[2018-01-13]

ドキュメンテーション文字列

関数本体の先頭の文字列リテラルを置いておく(Pythonの構文的には式文になる)と、ドキュメンテーション文字列として認識される。
ここには関数の説明を書く。(JavaのJavadoc相当)

def f():
    """Example1.

    example2
    example3
    """

    print("f")

ドキュメンテーション文字列は、関数オブジェクトの__doc__というフィールドに設定される。

print(f.__doc__)

↓実行結果

Example1.

    example2
    example3


help関数でも表示される。
ヘルプの表示では、文字列内のインデントが処理される。

help(f)

↓実行結果

Help on function f in module __main__:

f()
    Example1.

    example2
    example3

関数内の変数

関数の中で変数を使う事が出来る。
変数に値を代入せずにいきなり読み込もうとすると例外(NameError)が発生する。

パターン 説明
ローカル変数
def f():
    a = 1 # ローカル変数
    print(a)
関数内で新規に代入した変数は(何も宣言していなければ)ローカル変数になる。
グローバル変数
def f():
    global g # グローバル変数
    g = 1
f()
print(g) # グローバル変数なので関数が終わった後でも使える
グローバル変数を使う場合は、変数を使う前にglobal文で宣言する。
関数の外側の変数
def f1():
    m = 1
    n = 1
    print(f"f1-start {m} {n}")

    def f2():
        m = 2      # ローカル変数
        nonlocal n # f2の外側の変数
        print(f"f2-start {m} {n}")
        n += 1
        print(f"f2-end   {m} {n}")

    f2()
    print(f"f1-end   {m} {n}")

↓実行結果

f1-start 1 1
f2-start 2 1
f2-end   2 2
f1-end   1 2
関数内に関数を定義した場合、
nonlocal文を使うと、自分の関数の外側の変数を使うことが出来る。

戻り値の定義

戻り値はreturn文で返す。

return文を省略、もしくは戻り値を指定しない場合、Noneが返る。
(REPL(Pythonの対話モード)ではNoneが返ると何も表示されない)

パターン 呼び出す例 例の戻り値 説明
returnなし
def f0():
    pass
f0()
None return文を書かない場合はNoneが返る。
Scalaだとreturnを書かなくても最後の値が関数の戻り値になるが、
Pythonで値を返したければreturnが必須。
returnの戻り値なし
def f0():
    return
f0()
None return文に戻り値を指定しない場合はNoneが返る。
戻り値あり
def f1():
    return 123
r = f1()
123  
複数戻り値
def f2():
    return ("abc", 123)
def f2():
    return "abc", 123
t = f2()
('abc', 123) 複数個の戻り値を返す場合はタプルを使う。
(タプルは丸括弧を省略できる)
(a, b) = f2()
a, b = f2()
a='abc'
b=123

ジェネレーター関数

def文でジェネレーターを返す関数を定義することが出来る。
ジェネレーターは、for文のinで指定できるオブジェクト。(遅延評価され、一度だけしかループできないので、イテレーターのようなものか)

通常のreturn文では値を返すだけだが、return文の代わりにyield文を使うとジェネレーターを返せる。
(return文でもyield式ジェネレーター式を使ってジェネレーターを返すことは出来る)
(yield文もyield式も関数の中でしか使えない)

パターン 呼び出す例 例の結果 説明
ジェネレーター式
def g():
    return (x * 2 for x in range(3))
for i in g():
    print(i)
0
2
4
ジェネレーター式はリスト内包表記のような形式。
リスト内包表記は角括弧で囲むが、それを丸括弧にしたもの。
yield式
(Python 2.5)
def g():
    return (yield 123)
for i in g():
    print(i)
123 yield式は「値を1個だけ返すジェネレーター」を作るようだ。
yield from式
(Python 3.3)
def g():
    return (yield from [1, 2, 3])
for i in g():
    print(i)
1
2
3
yield式のfromではシーケンスを指定できる。
yield文
def g():
    yield 123
for i in g():
    print(i)
123 yield式を使う際のreturnと丸括弧が省略できるだけ^^;
yield from文
def g():
    yield from [1, 2, 3]
for i in g():
    print(i)
1
2
3
 
 
def g(n):
    i = 1
    while i <= n:
        yield i
        i += 1
for i in g(3):
    print(i)
1
2
3
ループの途中でyield文を使うことが出来る。[2018-01-15]

デコレーター

defの前にデコレーターを置くことが出来る。(Javaのアノテーション相当)
その関数(メソッド)が何らかの意味を持っていることを伝えるもの。

@デコレーター
…
def 関数名(引数, …): 複文

デコレーターは複数書くことが出来る。
各デコレーターの後は必ず改行する必要がある。

class MyClass:

    @staticmethod # staticメソッドであることを示すデコレーター
    def f():
        ...

関数アノテーション

関数の引数と戻り値にアノテーション(注釈)を付けることが出来る。(関数アノテーション
(→注釈付き代入文

def 関数名(引数名: パラメーターアノテーション, …) -> 戻り値アノテーション: 複文

これは要するに引数の型戻り値の型を示すもの。
引数名の後ろにコロン「:」を置き、その後ろに引数の型を書く。
引数リストの後ろに「->」を置き、その後ろに戻り値の型を書く。

これは単なるメタデータ情報であり、実際の値が指定された型と違ってもエラーになったりはしない(チェックされない。ただしチェックするツールもあるらしい)。
関数オブジェクトの__annotations__で関数アノテーションの情報を取得できる。

def f(arg1: str, arg2: int = 0) -> str:
    return arg1 + str(arg2)

print(f.__annotations__)

↓実行結果

{'arg1': <class 'str'>, 'arg2': <class 'int'>, 'return': <class 'str'>}

ちなみに関数アノテーションと聞くと、Javaならメソッドに付けるアノテーション、すなわち以下のようなものを想像する。

// Java
	@Override // アノテーション
	public String toString() {
		〜
	}

Pythonでも似たような構文がある。

    @staticmethod
    def f():
        ... 

が、これはPythonではデコレーターと呼ぶ。


型ヒント

注釈(アノテーション)によって変数や戻り値の型を書くことが出来るが、list等のコレクションの場合、要素の型を示す事が出来ない。[2018-01-13]
(注釈はあくまで何らかの値であり、Pythonではクラスもオブジェクトなので注釈の位置に書けるに過ぎない)

# listの例

def f(a: list):
    ...

このため、型ヒント(typing)というライブラリーが用意されている。(Python3.5)

# Listの例

from typing import List

def f(a: List[int]):
    ...

f([1, 2, 3]) # 要素がintのlistを渡す

この例では注釈をList[int]にしており、Listとlistに継承関係など無いが、実際に渡されるのがlistでも構わない。型チェックされるわけではないので。
(そもそもlist以外が渡されても、要素がint以外のlistが渡されても、エラーにはならない(苦笑))

Scalaだとジェネリクス付きの型をList[Int]の様に書くので、それと似た表現になっている。
 Pythonでは配列の添字の関数(__getitem__)を定義することが出来るので、それを使っているのだろう。技巧的だが、よく出来てる(笑))


関数オブジェクト

関数やクラスのメソッドはオブジェクトである。
つまり変数に代入したり引数に渡したりすることが出来る。

def func1(arg):
    print(arg)

func1(123) # 通常の呼び出し
f = func1  # 関数の代入
f(456)     # 関数オブジェクトを入れた変数からの関数呼び出し

ちなみにfunc1.__name__f.__name__で関数名(この例だといずれも「func1」)が取れる。


メソッドの例。

class MyClass:
    def __init__(self, arg):
        self.value = arg

    def m(self, arg):
        print(self.value + arg)
c = MyClass(1) # インスタンス生成
c.m(2)         # 通常のメソッド呼び出し
f = c.m        # メソッドの代入
f(3)           # 関数オブジェクトを入れた変数からのメソッド呼び出し

カリー化

カリー化するにはfunctoolsのpartialコンストラクターを使用する。[2018-01-15]

def f(a, b):
    print(f"a={a}, b={b}")

f(1, 2) # 通常の関数呼び出し

from functools import partial
f1 = partial(f, 3)   # 第1引数を3にする関数(カリー化)
f1(0) # → a=3, b=0

f2 = partial(f, b=3) # bを3とする関数(カリー化)
f2(0) # → a=0, b=3

ラムダ

ラムダは無名の関数。

lambda 〔引数, …〕: 式

ラムダは以下の関数定義と同等。(ただし関数名は無い。ラムダを入れた変数fに対し、f.__name__は「<lambda>」になる)

def 無名(引数, …):
    return 式

※ラムダの本体には式をひとつだけ書く。つまり複数の文を書くことは出来ない。
※ラムダの引数にはアノテーション(型ヒント)を書けない。コロン「:」が来ると引数リストの終わり(式の始まり)になる為。[2018-01-13]

パターン 同等の例 Scala相当
引数なし
f = lambda: 123



print(f())
def func():
    return 123

f = func
print(f())
val f = () => 123



println(f())
引数1個
f = lambda n: n + 1



print(f(9))
def func(n):
    return n + 1

f = func
print(f(9))
val f = (n: Int) => n + 1



println(f(9))
引数2個
f = lambda x, y: x + y



print(f(1, 2))
def func(x, y):
    return x + y

f = func
print(f(1, 2))
val f = (x: Int, y: Int) => x + y



println(f(1, 2))
フィルターの例
a = [1, 2, 3, 4]




list(filter(lambda n: n % 2 == 0, a))
a = [1, 2, 3, 4]

def f(n):
    return n % 2 == 0

list(filter(f, a))
val a = Seq(1, 2, 3, 4)




a.filter(_ % 2 == 0)

Pythonへ戻る / 技術メモへ戻る
メールの送信先:ひしだま