2016-06-20 3 views
0

多くの場合、複数のサブファンクションのパラメータを変更できるトップレベルの機能がある状況に遭遇します。私は、次の例としてこれを表現するだろう:私は私の内側の機能のためのすべてのデフォルトを再定義することを強制していますので、複数のサブファンクションをパラメータ化するためのベストプラクティス

def plot_data_processing(data_param_1=3, data_param_N=4, 
     processing_param_1='a', processing_param_2='b', plotting_param_1='c', 
     plotting_param_2=1324): 
    data = get_data(data_param_1=data_param_1, data_param_1=data_param_N), 
    processed_data = process_data(data, processing_param_1=processing_param_1, processing_param_2=processing_param_2) 
    plot_data(processed_data, plotting_param_1=plotting_param_1, plotting_param_2=plotting_param_2) 

は今、これは、醜いの一種である、と私のパラメータは、一つの大きな混乱しています。私は、次の操作を行うことができたとします。私は、彼らが唯一検証するために呼び出される関数を待つ辞書を経由して引数を渡すのこの奇妙な練習を、やっているので

def plot_data_processing(data_kwargs, processing_kwargs, plotting_kwargs): 
    data = get_data(**data_kwargs), 
    processed_data = process_data(data, **processing_kwargs) 
    plot_data(processed_data, **plotting_kwargs) 

plot_data_processing(dict(data_param_1=3, data_param_N=4), dict(processing_param_1='a', processing_param_2='b'), dict(plotting_param_1='c',plotting_param_2=1324)) 

それでも、これは、素晴らしいではありません。バグや判読不能なコードのレシピのようです。また、私は、同様のインターフェースを使って、さまざまな機能のために内部的に呼ばれる機能を交換する自由を持っていません。だから、私も行くことができる:

def plot_data_processing(data_getter, data_processor, plotter): 
    data = data_getter(), 
    processed_data = data_processor(data) 
    plotter(processed_data) 

class DataGetter(object): 
    def __init__(self, data_param_1=3, data_param_N=4): 
     self.data_param_1 = data_param_1 
     self.data_param_N = data_param_N 
    def __call__(self): 
     # .... 
     return data 

# ... Also define classes DataProcessor and Plotter 

plot_data_processing(DataGetter(data_param_1=3, data_param_N=4), DataProcessor(processing_param_1='a', processing_param_2='b'), Plotter(plotting_param_1='c',plotting_param_2=1324)) 

しかし、これはまた、不要な構造とフラフコード(self.x = xとすべて)を含むようだ。 Iパーシャル(またはラムダ)を使用して、その周りを取得することができます:

def plot_data_processing(data_getter, data_processor, plotter): 
    data = data_getter(), 
    processed_data = data_processor(data) 
    plotter(processed_data) 

# Called like: 
plot_data_processing(
    data_getter = partial(get_data, data_param_1=3, data_param_N=4), 
    data_processor = partial(process_data, processing_param_1='a', processing_param_2=3), 
    plotter = partial(plot, plotting_param_1='c', plotting_param_2=1342), 
    ) 

しかし、これはまた、満足のいかないようだ - との関数を呼び出す引数の明確な「タイプ」は存在しないため - 動作するはずただ一部の機能この関数を使用したい別のプログラマーにとっては、より困難になります。

だから、これらの方法のどれも、私に満足感や幸せを感じさせません。私は部分的に好きだと思いますが、部分的な関数がいくつかのインターフェースに従うことを宣言するための方法が欲しいです。

もっと良い方法を知っている人はいますか?

+1

*「一部の機能が一部のインターフェースに従うと宣言する」*とはどういう意味ですか? – jonrsharpe

+0

たとえば、私の "data_getter"関数は引数を取らずに何らかのデータオブジェクトを返すべきです。私の "data_processor"は何らかのデータオブジェクトを取り、処理されたデータオブジェクトを返すべきです。私はこれらのインターフェースに従うために渡される関数が必要であり、それらの関数を呼び出す前に行うことを宣言することができます。これはクラス(assert isinstance(data_getter、DataGetter))で行うことができますが、data_getterオブジェクトが部分的な関数でDataGetterインターフェイスが関数の入出力を指定している場合は同等の機能を探しています。クラス。 – Peter

+1

これは、あなたがPythonでやっていることではありません。一般的に私たちはダックタイピングに固執しています。適切なインターフェースを持つものを渡すと、すべてが動作します。そうでなければ、 'TypeError'(またはその他の例外)を取得します。これに関する関数とメソッドに違いはありません。 – jonrsharpe

答えて

1

Python 3.5には、(オプションの)タイプのヒントシステムが新しく追加されました。実行時にはPythonインタプリタによってチェックされませんが、引数の型や関数の値を返すことができます。 mypyのような独立したスタティックアナライザープログラムをコード上で実行して、タイピングエラーを探すことができます。あなたのplot_data_processing機能については

、私はあなたが物事を宣言したいと思いますこのようなものだと思う:data_processer機能は同じを返す場合にのみ、1 DataTypeではなく2で逃げることができるかもしれません

from typing import Callable, TypeVar 

DataType = TypeVar("DataType") 
ProcessedDataType = TypeVar("ProcessedDataType") # could be the same as DataType 

def plot_data_processing(data_getter: Callable[[], DataType], 
         data_processor: Callable[[DataType], ProcessedDataType], 
         plotter: Callable[[ProcessedDataType], None]) -> None: 
    ... 

を処理されたデータは元のデータと同じタイプを使用します。一般的なアプローチが不要な場合は、これらのタイプを具体的に指定することもできます(を使用するのではなく、Sequence[float]など)。

詳細はPEP 484とドキュメントthe typing moduleを参照してください。

関連する問題