S-JIS[2018-01-08/2018-01-15] 変更履歴
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' |
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 |
ジェネレーター式はリスト内包表記のような形式。 リスト内包表記は角括弧で囲むが、それを丸括弧にしたもの。 |
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 |
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 |
|
def g(n): i = 1 while i <= n: yield i i += 1 |
for i in g(3): print(i) |
1 |
ループの途中で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) |