2015-12-22 9 views
6

最初のパラメータの型だけでなく、任意の述語に基づいて、関数のさまざまな実装をディスパッチできます。現在、私はそうのようにそれをしなければならない。Pythonでの関数パターンと述語の照合

def f(param): 
    try: 
     if param > 0: 
      # do something 
    except TypeError: 
     pass 
    try: 
     if all(isinstance(item, str) for item in param): 
      # do something else 
    except TypeError: 
     raise TypeError('Illegal input.') 

は、ここで私が行うことができるようにしたいのですが何の精神で何か:

@generic 
def f(param): 
    raise TypeError('Illegal input.') # default 

@f.when(lambda param: param > 0) 
def f_when_param_positive(param): 
    # do something 

@f.when(lambda param: all(isinstance(item, str) for item in param)) 
def f_when_param_iterable_of_strings(param): 
    # do something else 

しかし、Pythonの3のsingledispatchに似てsingledispatch任意の述語ではなく、型のディスパッチのみをサポートします。

TL; DR:パラメータの型だけでなく、任意の述語に基づく関数の述語ベースのディスパッチを可能にするライブラリがありますか?

答えて

2

をのおかげでレプリケーター。この質問をした後、正確にこれを行う既存のモジュールがないように見えました。だから私は自分自身を書いた:)それは@ Elazarの示唆に触発されている。

お気軽にご確認ください。 It's on PyPIあなたが使用してそれをインストールすることができます。

pip install genericfuncs 

またhosted on GitHubだと私は開発を継続し、APIをシンプルにしようとしているときに機能を追加する予定。寄付を歓迎します。

-1

あなたは次のように入力の特性を確認するためにisintanceを使用してABCとそれを組み合わせることができます。

from collections.abc import Iterable 

def foo(param): 
    if isinstance(param,int) and param > 0: 
     #do something 
    elif isinstance(param,Iterable) and all(isinstance(item, str) for item in param): 
     # do something else 
    else: 
     raise TypeError('Illegal input.') 

ABCはあなたを教えparamは持っているインタフェースの種類、あなたが適切に使用できるように1つは特定の型であるかどうか気にしない場合に行います。そのように定義すると、そのパラメータはset,またはtupleの文字列になり、常に2番目のチェックを過ぎて適切に処理できます。また、ABCはnumbersであり、その場合も一般的になりたいですか?

1

私はライブラリについてはわかりませんが、基本的な実装の骨子はここにあります。私の考えでは、これを実用的な解決策にすることを妨げる本当の問題は、私がここで専門的な解決策をどう働かせるかわからないことです。。そのような場合、おそらくメンテナンスの難しさにつながります。

#!/usr/bin/python3 

class when(object): 
    funcs = {} 

    def __init__(self, pred): 
    self.pred = pred 

    def __call__(self, func): 
    if func.__qualname__ not in when.funcs: 
     when.funcs[func.__qualname__] = {} 

    when.funcs[func.__qualname__][self.pred] = func 

    return lambda *args, **kwargs: when.__match(func, *args, **kwargs) 

    @staticmethod 
    def __match(f, *args, **kwargs): 
    for pred, func in when.funcs[f.__qualname__].items(): 
     if pred(*args, **kwargs): 
      return func(*args, **kwargs) 
    raise NotImplementedError() 


@when(lambda x: x < 0) 
def my_func(x): 
    return "smaller!" 

@when(lambda x: x > 0) 
def my_func(x): 
    return "greater!" 


print(my_func(-123)) 
print(my_func(123)) 

[1]:分解能の問題は、それが権利を取得することは容易ではないということです。考慮すべきいくつかの選択肢がありますが、そのすべてに実装と使用の理由が厳しくありません。

  1. 適用する特殊な述語は複雑なものであり、おそらくそれぞれのランク/ウェイトを手動で定義するためにユーザーの手元に置くのが最適です。これは不器用で、一般的にこのメカニズムの初期の魅力に値するものではないメンテナンス/ボイラープレートの頭痛です。
  2. ユーザは、プログラムが実行されている(Pythonは解釈されている)ので、常にオーバーロードを追加することができ、これは驚くべき時間的振る舞いを引き起こす可能性があります。あなたのコードベースでこれを広めることは満足しています。周りに広がっていないときは、ただif/elseで終わるのはなぜですか?
  3. 何らかの理由で使用を制限し、特定の呼び出しに対して1つの述部だけがTrueを返すようにすることができます。これは、奇妙で、非効率的で、多くの点で役に立たない。サブクラスCを特別な方法で扱いたいのであれば、どうしたらいいですか?あるいは、追加の条件で述語をさらに専門化したい場合。このようなモデルをどのように分類するのですか?
1

@ヤムズからあなたの構文に合わせて調整され、ライブラリとして使用される解決策があります。(一般的なものである)の決定は、最初の述語が勝ちということである。

class guarded: 
    def __init__(self, default): 
     self.funcs = [] 
     self.default = default 

    def when(self, pred): 
     def add(func): 
      self.funcs.append((pred, func)) 
      return func 
     return add 

    def __call__(self, *args, **kwargs): 
     for pred, func in self.funcs: 
      try: 
       match = pred(*args, **kwargs) 
      except Exception: 
       match = False 
      if match: 
       return func(*args, **kwargs) 
     return self.default(*args, **kwargs) 

ユーザーコード:

@guarded 
def f(param): 
    raise TypeError('Illegal input') 

@f.when(lambda param: param > 0) 
def f_when_param_positive(param): 
    return 'param_positive' 

@f.when(lambda param: all(isinstance(item, str) for item in param)) 
def f_when_param_iterable_of_strings(param): 
    return 'param_iterable_of_strings' 

がそれをしようと、我々のような何かを得る:

>>> print(f(123)) 
param_positive 
>>> print(f(['a', 'b'])) 
param_iterable_of_strings 
>>> print(f(-123)) 
Traceback (most recent call last): 
... 
TypeError: Illegal input 
+0

Oooh。どうやら正確で完全な答えが下落したのはどうですか?私は疑問に思う。 – Elazar

関連する問題