kb84tkhrのブログ

何を書こうか考え中です あ、あと組織とは関係ないってやつです 個人的なやつ

実験:関数オーバーロード(続き)

pythonは型ヒントを無視するのでこうなるしかないんですけど

どうしてもオーバーロードするなら自分で型を判定する
ていうかUnion型があるってことはもともとそういうものってことかな

class Point:

    def __init__(self,
                 x: Union[Tuple[float, float], float] = 0.0,
                 y: float=0.0) -> None:
        if isinstance(x, tuple):
            self.x = x[0]
            self.y = x[1]
        else:
            self.x = x
            self.y = y

mypyは通った
動かす

$ python3 -i shape.py
>>> Point()
Point(0.0, 0.0)
>>> Point(1.0,2.0)
Point(1.0, 2.0)
>>> Point((2.0,4.0))
Point(2.0, 4.0)
>>> Point(3.0)
Point(3.0, 0.0)

Ok
最後のはおまけだけど、そうなるよな

こうは書けない?

        if isinstance(x, Tuple[float, float]):
            self.x = x[0]
            self.y = x[1]

書けない

$ python3 -i shape.py
>>> Point()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "shape.py", line 18, in __init__
    if isinstance(x, Tuple[float, float]):
  File "/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/typing.py", line 1261, in __instancecheck__
    raise TypeError("Parameterized Tuple cannot be used "
TypeError: Parameterized Tuple cannot be used with isinstance().

Parameterized Tupleはダメって言ってるな
ということはこれならいけるのか?

        if isinstance(x, Tuple):
            self.x = x[0]
            self.y = x[1]

いけた

$ python3 -i shape.py
>>> Point((2.0, 4.0))
Point(2.0, 4.0)

tupleと書くかTupleと書くかでなにか違ってくるのかな
なんとなくTupleと書いたほうが一貫性はありそうな気がする

オーバーロード的なことをやるfunctools.singledispatchというデコレータもあるようだけど
関数をデコレートするもので、メソッドをデコレートするものではない
そりゃそうだよな
メソッドはそもそもselfでシングルディスパッチするんだから

と思ったら
singledispatchを使ってメソッドでも同等のことができるようにする技が
stackoverflowには載っていた

python - How can I use functools.singledispatch with instance methods? - Stack Overflow

こういうデコレータを作って使う

from functools import singledispatch, update_wrapper

def methdispatch(func):
    dispatcher = singledispatch(func)
    def wrapper(*args, **kw):
        return dispatcher.dispatch(args[1].__class__)(*args, **kw)
    wrapper.register = dispatcher.register
    update_wrapper(wrapper, func)
    return wrapper

このコードを理解した上で使うかというとisinstanceとifで
いいんじゃないのって気もする

関数とかメソッドのオーバーロードって、既存のソースをいじらずに
新しい型に対応して拡張できるってところがいちばん嬉しいところだと思うので
一箇所にまとめて書くとなるとメリットが減るし