■ はじめに
Scalaで高階関数(こうかいかんすう)を知る機会があったが Pythonでもfunctoolsモジュールで扱えるとのことなので メモってみた。
目次
【1】高階関数 (Higher-Order Functions) 【2】functools 【3】cache 1)@cache 2)@cached_property 【4】partial 1)partial 2)partialmethod 【5】reduce 【6】wrap 【7】single dispatch 1)@singledispatch 2)@singledispatchmethod
【1】高階関数 (Higher-Order Functions)
* 引数として関数を受け取ったり 戻り値として関数を返す関数 => 戻り値をオブジェクトとして扱う次元の高い関数のため 「高階関数 (Higher-Order Functions)」と呼ぶ
特徴
* 以下の特徴を持つ [1] 関数を引数として受け取る [2] 関数を返す
【2】functools
* 高階関数を扱いやすくする機能を提供するPython標準ライブラリ
https://docs.python.org/ja/3/library/functools.html
【3】cache
* 同じ引数を入れた時、最初の計算結果を即座に返す
1)@cache
from functools import cache # 再帰関数 @cache def factorial(n): print('call', n) return n * factorial(n-1) if n else 1 result = factorial(10) print(f'result = {result}') result = factorial(7) print(f'result = {result}')
出力結果
call 10 call 9 … call 1 call 0 result = 3628800 # キャッシュされているから「call X」が表示されることなく値が返されている result = 5040
2)@cached_property
from functools import cached_property class Number: def __init__(self, number_list): self.number_list = number_list @cached_property def sum(self): print('Call') return sum(self.number_list) number = Number([1, 2, 3]) print(number.sum) print(number.sum)
出力結果
Call 6 6
【4】partial
* 部分的に引数を固定する
1)partial
from functools import partial def say_something(greeting, name): print(f'{greeting}, {name}!!!') # 注目 say_hello = partial(say_something, 'Hello') say_hello('Mike')
2)partialmethod
* クラスのメソッドのpartial版
サンプル
from functools import partialmethod class Animal: def __init__(self): self._alive = False @property def alive(self): return self._alive def set_state(self, state): self._alive = bool(state) set_alive = partialmethod(set_state, True) set_dead = partialmethod(set_state, False) animal = Animal() print(animal.alive) # False animal.set_alive() print(animal.alive) # True animal.set_dead() print(animal.alive) # False
【5】reduce
* 2 つの引数をもつ function を左から右に累積的に適用し
サンプル
from functools import reduce numbers = [1, 2, 3, 4, 5] # ((((1+2)+3)+4)+5) total = reduce(lambda x, y: x + y, numbers) print(total) # 15
【6】wrap
* 関数が他の関数をラップしていることを示すために使用されるデコレータ
通常は、、、
* 自作デコレータを使うためにラッパー関数を定義するとき、 何もケアしなければラップされた関数の情報が覆い隠されてしまう => この関数は、この内部情報を幾分か見れるように定義可能
from functools import wraps def my_decorator(f): @wraps(f) def my_wrapper(*args, **kwds): print("Calling decorated function - start") print(args) print(kwds) print("Calling decorated function - end") return f(*args, **kwds) return my_wrapper @my_decorator def demo(*args, **kwds): """Docstring""" print("Called example function") demo(123, "abc", a=1, b=1) print('***************') print(demo.__name__) print(demo.__doc__)
出力結果
Calling decorated function - start (123, 'abc') {'a': 1, 'b': 1} Calling decorated function - end Called example function *************** demo Docstring
出力結果 (「#@wraps(f)」した場合)
Calling decorated function - start (123, 'abc') {'a': 1, 'b': 1} Calling decorated function - end Called example function *************** my_wrapper None
【7】single dispatch
* ジェネリック関数を定義できる
1)@singledispatch
from functools import singledispatch @singledispatch def say_hello(x): print("Hello,", x) def say_int(x): print("Hello, integer", x) def say_str(x): print("Hello, string", x) say_hello.register(int, say_int) say_hello.register(str, say_str) say_hello(123) # Hello, integer 123 say_hello('Mike') # Hello, string Mike say_hello(12.3) # Hello, 12.3
2)@singledispatchmethod
from functools import singledispatchmethod from decimal import Decimal class Demo: @singledispatchmethod def say_hello(self, value): print(f'Hello, {value}!!') @say_hello.register def _(self, obj: int): print("int ", obj) @say_hello.register(float) @say_hello.register(Decimal) def _(self, obj): print("float or Decimal ", obj) demo = Demo() demo.say_hello(1) # => int 1 demo.say_hello(1.23) # => float or Decimal 1.23 demo.say_hello(Decimal("4.56")) # => float or Decimal 4.56 demo.say_hello("Mike") # => Hello, Mike!!
参考文献
https://smooth-pudding.hatenablog.com/entry/2021/03/27/145218
https://docs.kanaries.net/ja/topics/Python/functools-python3
https://marusankakusikaku.jp/python/standard-library/functools/
関連記事
Python ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/08/07/231242
Python ~ 基本編 / 文字列 ~
https://dk521123.hatenablog.com/entry/2019/10/12/075251