2012-02-23 17 views
5

コマンドラインオプション(argparse、getopt、blargsなど)の解析と調整には、多くのPythonモジュールがあります。そして、Pythonは様々な関数の引数(例えば、デフォルト値、* varargs、** keyword_args)を扱うための優れた組み込みの機能/イディオムに恵まれています。しかし、トップレベル関数のためのさまざまなプロジェクトのコードを読んだとき、私はコマンドライン引数よりも関数引数の規律と標準化が顕著に少なくなっています。関数引数の処理に推奨されるPythonモジュール?

単純な関数の場合、これは問題ではありません。ビルトインの引数の機能は十分に機能しています。しかし、トップレベルの機能がさまざまな引数とオプション(補完的または排他的なもの)、さまざまな操作モード、デフォルト、オーバーライドなどを多数提供する機能豊富なモジュールが数多くあります。つまり、引数の複雑さがありますコマンドライン引数のそれに近づく。そして、彼らは主に彼らの議論をアドホック方法で扱っているようです。

コマンドライン処理モジュールの数が増え、時間が経ってどのように洗練されたのかを考えれば、複雑な関数引数の論争を単純化するために少なくとも少数のモジュールが必要になると思います。しかし、私はPyPi、stackoverflow、Googleを検索して成功していません。だから...あなたが推奨する引数を扱う引数(コマンドラインではありません)がありますか?

---例で更新---

あなたは洗練されたモジュールを扱っているまで、ユースケースが表示されないので、それは本当に簡単な具体例を与えることは難しいです。しかし、ここではコードの問題を説明しています:フォーマッタのインスタンス化でオーバーライドできるデフォルトのフォーマッタモジュール、または関数/メソッドが呼び出されたとき。ほんの僅かなオプションしか持たないので、すでにオプション処理の言葉が大量にあり、オプション名はad nauseamと繰り返されます。

defaults = { 'indent':  4, 
       'prefix': None, 
       'suffix': None, 
       'name':  'aFormatter', 
       'reverse': False, 
       'show_name': False 
      } 

class Formatter(object): 

    def __init__(self, **kwargs): 
     self.name = kwargs.get('name', defaults['name']) 
     self.indent = kwargs.get('indent', defaults['indent']) 
     self.prefix = kwargs.get('prefix', defaults['prefix']) 
     self.suffix = kwargs.get('suffix', defaults['suffix']) 
     self.reverse = kwargs.get('reverse', defaults['reverse']) 
     self.show_name = kwargs.get('show_name', defaults['show_name']) 

    def show_lower(self, *args, **kwargs): 
     indent = kwargs.get('indent', self.indent) or 0 
     prefix = kwargs.get('prefix', self.prefix) 
     suffix = kwargs.get('suffix', self.suffix) 
     reverse = kwargs.get('reverse', self.reverse) 
     show_name = kwargs.get('show_name', self.show_name) 

     strings = [] 
     if show_name: 
      strings.append(self.name + ": ") 
     if indent: 
      strings.append(" " * indent) 
     if prefix: 
      strings.append(prefix) 
     for a in args: 
      strings.append(a.upper() if reverse else a.lower()) 
     if suffix: 
      strings.append(suffix) 
     print ''.join(strings) 

if __name__ == '__main__': 
    fmt = Formatter() 
    fmt.show_lower("THIS IS GOOD") 
    fmt.show_lower("THIS", "IS", "GOOD") 
    fmt.show_lower('this IS good', reverse=True) 
    fmt.show_lower("something!", show_name=True) 

    upper = Formatter(reverse=True) 
    upper.show_lower("this is good!") 
    upper.show_lower("and so is this!", reverse=False) 
+0

あなたはdef somefunc(requiredValue、optionalValue = None、* args、** kwargs)について話していますか?そのようにすれば、あなたの関数にコード化することができます。これは、ターミナルから来るコマンドラインargs(関数自体)を扱うようです。 – platinummonkey

+3

複雑なインターフェースを持つ関数の例と、ライブラリから期待される単純化のアイデアを教えてください。 (それは価値があると思いますが、私はそこにいくつかのPythonモジュールがあると思いますが、それはあなた自身のようなものをやろうとするよりも抑止的な例として考えることをお勧めします)。 –

+0

@SvenMarnach確かに非常に注意深い、避けてすべてのコストのある例がそこにあります!しかし、ファンクション(ファクトリ関数など)が洗練された機能のエントリポイントである場合、複雑であることが必ずしもエラーであるとは限りません。それは単に手元の仕事の複雑さ/変動性を反映しているだけかもしれません。 –

答えて

1

あなたのAPIは、あなたがあなたを渡されたオプションを処理するためにいくつかのモジュールを使用する方が簡単だと思うので、複雑な場合は、実際ソリューションは、あなたのAPIを簡単にすることである可能性が高いと言えます。いくつかのモジュールが非常に複雑なものを呼び出す方法を持っているという事実は、機能ではなく、残念です。

私が最初にあなたの質問を読ん
3

は、私はあなたがバンドエイドモジュールを求め、そして誰もが悪いデザインが持続することができますモジュールを書きたいとは思いませんので、それが存在しないことをしていることを自分自身に考えました。

しかし、私は状況がそれより複雑であることに気付きました。あなたが記述するモジュールの作成のポイントは、再利用可能な一般的なコードを作成することです。さて、正当な理由で複雑なインターフェースがいくつかあるかもしれません。しかし、これらのインターフェイスは、正確にはです。これはおそらく一般的なケースのコードでは簡単に処理できません。彼らは特殊なケースがたくさんある問題のあるドメインに対処するため、複雑です。

つまり、インターフェイスが実際にをリファクタリングできない場合、おそらくモジュールで一般化する価値があるほど予測できないカスタムの特殊ケースコードがたくさん必要になるでしょう。インターフェースは簡単にあなたが記述の種類のモジュールにパッチを適用できるのであれば逆に、おそらくリファクタリングすることができます - その場合にはそれがあるべき。

+0

同意しない。この引数に続いて、コマンドラインの処理をargparse、getoptなどに一般化するのは意味がありません。しかし、そうしています。私が求めているのは、モジュールと関数のアナログです:複雑な関数を一般化し簡素化するコード - 必要に応じて、時にはそれはargparseがコマンドに役立つのと同じ方法です! PyPiでのコードの読解は、この使用を単純化することが有用であることを示唆しています。そして、それは不可能ではありません - 私は良い解決策を持っています。受け入れられた道があるかどうかを調べるだけです。たくさんの "ちょうどリファクタリング"!答え、私はそこにないと推測している。それでも。 –

+0

@JonathanEunice、まあ、それは不可能だとは決して言いませんでした。正当に複雑なコードベースの複雑さを減らすのではなく、むしろ増加させる可能性が高いと言いました。より一般的には、コマンドラインインターフェイスとAPIの類推は間違っていると私は考えています。彼らは本当にあまり似ていません。 – senderle

+0

IMOの程度の違いではなく、コマンドラインと関数はそれぞれ、オプション、命令、およびデータのセットを使用して、いくつかの機能を呼び出します。幸いなことに、関数はしばしば単純であり、よりリファクタリング可能です。しかし、私がPyPiから見たコードでは、機能を単純化する必要があることが示唆されています。 YMMV。 –

0

開発者の手でその、しかし、あなたには、いくつかの他のプロジェクトのために有用である可能性があるか、他のユーザー間で公開されますライブラリを作っているなら、私はあなたの問題を特定し、それを分析する必要が最初だと思う、

あなたの関数をうまく記述してください。引数の数を最小限に抑えれば、ユーザが渡す必要があるものを指定するのが難しい関数引数のデフォルト値を提供します。

classmethods特別な場合は、高度なプログラミングのために上書きすることも、実際にライブラリで再生していることを達成したい上級ユーザーが提供することもできます。継承は常に存在します。

、あなたは役に立つかもしれませんが、最終的な目標はオプションの引数にデフォルト値を提供することをいい、必要な引数を入力するユーザーを制限、引数の最小数を指定することでもPEP8読むことができます - あなたのlibrary方法での/ codeは普通の開発者でも簡単に理解できます。

0

デフォルトのコードをもっと書くことができます。

他の方法でデフォルトにすることを考えるなら、デフォルトを通り、存在しない場合はキーワードを上書きしてください。

defaults = { 'indent':  4, 
      'prefix': None, 
      'suffix': None, 
      'name':  'aFormatter', 
      'reverse': False, 
      'show_name': False 
     } 

class Formatter(object): 

    def __init__(self, **kwargs): 
     for d,dv in defaults.iteritems(): 
     kwargs[d] = kwargs.get(d, dv) 

側注: 私は、デフォルトで__init__メソッド定義にキーワード引数を使用してお勧めします。これはfunction定義は、本当に私は、コマンドラインの解析と関数の引数の処理は多くの共通点を持っていないと思うあなたのクラスの他の開発者とユーザ(Formatter

def __init__(self, indent=4, reverse=False .....etc.....): 
+0

'kwargs [d] = kwargs.get(d、dv)'は 'kwargs.setdefault(d、dv)'と書くことができます。それが 'setdefault()'と呼ばれる理由です。まだ値が設定されていない場合、デフォルト値に設定されます。 –

2
  1. との契約になることができます。コマンドラインの主な問題は、利用可能なデータ構造だけが文字列のフラットなリストであり、各文字列が何を意味するかを定義するために使用できる関数ヘッダーのような計測器がないことです。 Python関数のヘッダーでは、各パラメーターに名前を付けることができます。パラメーターとしてコンテナーを受け入れることも、デフォルトの引数値を定義することもできます。コマンドライン解析ライブラリは、機能Pythonは関数呼び出しのための関数を提供しています:名前をパラメータに渡し、デフォルト値を代入し、必要な型に変換します。Pythonでは、これらのすべての機能が組み込まれているので、ライブラリを必要としません。

  2. この例では、言語が提供する機能を使用してこのデザインを改善する方法が数多くあります。 defaults辞書の代わりにデフォルトの引数値を使うことができます。すべてのフラグをFormatterConfigクラスにカプセル化し、これらの引数のすべてではなく1つの引数だけを渡すことができます。しかし、あなたが例のコードで与えたインタフェースを正確に欲しいと仮定しましょう。これを実現する1つの方法は次のコードです:

    class Config(dict): 
        def __init__(self, config): 
         dict.__init__(self, config) 
         self.__dict__ = self 
    
    def get_config(kwargs, defaults): 
        config = defaults.copy() 
        config.update(kwargs) 
        return Config(config) 
    
    class Formatter(object): 
    
        def __init__(self, **kwargs): 
         self.config = get_config(kwargs, defaults) 
    
        def show_lower(self, *args, **kwargs): 
         config = get_config(kwargs, self.config) 
    
         strings = [] 
         if config.show_name: 
          strings.append(config.name + ": ") 
         strings.append(" " * config.indent) 
         if config.prefix: 
          strings.append(config.prefix) 
         for a in args: 
          strings.append(a.upper() if config.reverse else a.lower()) 
         if config.suffix: 
          strings.append(config.suffix) 
         print "".join(strings) 
    

    Pythonはこの種の引数処理を行うツールを多数提供しています。したがって、(デフォルトの引数のように)それらのいくつかを使用しないことを決めたとしても、やり直しを避けることができます。

+0

ポイント1:はい!うまく置く。 Python関数は、コマンドライン引数のフラット文字列よりもはるかに豊富に開始されます。 ポイント2:Concur。もしConfig/get_configがモジュール内にあれば、私が求めていたことをうまく開始することができるでしょう... Pythonのargを拡張する設定のデフォルト、オーバーライドなどの、高度にオプションの機能をサポートします。驚いたことに、すでにPyPiには洗練された、よく知られたモジュールはありません。 –

関連する問題