2011-08-18 11 views
5

私はクライアントコードで実装される「インターフェース」があります。一般的な見返りにdocutilsnodeこの場合、「Pythonic」と「convenient」のバランスはどうすればできますか?

class Runner: 
    def run(self): 
     pass 

runをすべきであるが、遠く最も 一般的なケースはプレーンテキストであるため、呼び出し側はrunを返すことができます文字列はを使用してチェックされ、nodeに変わります。 になります。しかし

、私は「Python的」理解の方法は、これではない「Python的」理由 チェックすることで、それはタイプ「で」できません何かのtype() 1のように「演技」 - すなわち「Python的」コードはダックタイピングを使用する必要があります。

私は

def run_str(self): 
    pass 

def run_node(self): 
    return make_node(self.run_str()) 

を検討したが、それは名前で右がそれほど面白くない戻り値の型 を置くので、私はこのために気にしません。それは気を散らす。

私が見逃したアイデアはありますか?また、私が "悪い"システム(それは多かれ少なかれ私にとって安全であると思われる)と道路の下に が遭遇するかもしれない問題はありますか?あなたはこのような何かを行うことができErrors and Exceptions.

+0

私は少し混乱しています。 'run'に渡される値について(' arg'を介して)話していますか?あるいは、 'Runner'インターフェースを実装しているオブジェクトの' run'メソッドによって返された仮説的値をどう扱うかについて話していますか? – senderle

+0

私はリターンを意味します。私は 'arg'を編集してしまいました(これを指摘してくれてありがとう)。 – Owen

答えて

5

これはやや誤解を招く例です。あなたが言及していないことがあります。私はあなたが "インターフェイスを持っている"と言ったとき、オブジェクトを受け取り、そのrunメソッドを呼び出すコードをいくつか持っているということを推測しています。

runメソッドを呼び出す前にそのオブジェクトの型をテストしていない場合は、単純で単純なダックタイピングを使用しています。 (この場合、runメソッドがある場合はRunnerです。)runメソッドのオブジェクトにtypeまたはisinstanceを使用しない限り、あなたはPythonicです。

普通の文字列を受け入れるべきか、ノードオブジェクトだけを受け入れるべきかという質問は、微妙に異なる質問です。文字列とnodeオブジェクトは、おそらく同じインターフェイスを実装していません。文字列は基本的にはでのようにのような突っ込みがないので、それらを1つのように扱う必要はありません。これは象のようなものです。もしあなたがそれをアヒルのように突き刺すのであれば、象をテーププレーヤーに与え、最初に象を訓練する必要があります。

これはもう「ダックタイピング」の問題ではなく、インターフェイスデザインの問題です。あなたはあなたのインターフェースをどれだけ厳密にしたいかを決めようとしています。

答えを与えるために、このレベルでは、runnodeオブジェクトを返すと仮定するのが最もPythonicだと思います。そのためには、isinstanceまたはtypeを使用する必要はありません。ちょうどそれがnodeオブジェクトのふりをして、あなたのインタフェースを使用しているプログラマが間違って、例外を見た場合、彼らはrunnodeオブジェクトを渡すべきであることを伝えるあなたのdocstringを読む必要があります。

の場合は、にも文字列、または文字列のような突っ込みを受け入れるようにしたい場合は、そうすることができます。文字列はむしろプリミティブ型なので、isinstance(obj, basestring)(ただし、ではなく、type(obj) == str)を使用することは不適切ではないと言います。これは、ユニコード文字列を拒否するためです。基本的に、これはあなたのプログラムの怠惰なユーザーにとって非常にリベラルで親切です。あなたは既に象を受け入れ、アヒルのように突っ込んでいるものを受け入れることによって、上を行き来しています。

(より具体的には、私はこれは少しあなたが発電機や配列の両方を受け入れるようにしたい関数の先頭で引数にiterを呼び出すようなものであると思います。)

+0

これは、Pythonの純度と実用的なAPI設計との優れたトレードオフです。私はまた、あなたのユーザーが何も返さないことで、誰も "返す"ことを期待しています。 – PaulMcG

1

チェックアウト:あなたのmake_node関数内

def run(self,arg): 
    try: 
     return make_node(arg) 
    except AlreadyNodeError: 
     pass 

は、引数がすでにノードであれば、それはAlreadyNodeErrorを上げています。

+0

+1私の考えは正確に(ほぼ正確に)。 – zeekay

+0

unshosherの呼び出しを 'type()'に 'make_node()'関数に移していませんか? – Owen

+0

make_node関数にTry:Raise:ブロックがあり、ノードに作成しようとしましたが、すでにノードであることがわかったときにエラーが発生します。 – Jonathanb

2

特に、単純な操作がすべて発生する場合は、それぞれの型を処理するメソッドを持つ必要はありません。

def run(self): 
    try: 
     ...assume it's a str 
    except TypeError: 
     ...oops, not a str, we'll address that 

これは、一般的に、より速く、より単純であるコードのEasier to ask for forgiveness than permission (EAFP)スタイルを、次の共通Python的なアプローチは、のような何かをするだろう。変数の型を検出するためのtype()を使用し

+0

私たちはさまざまなことを話していると思います。私は戻り型を意味しました。あなたの答えはそこにも適用されます - 私の場合はうまくいくかもしれません。 – Owen

+0

私は、メソッドが完全に異なるタイプのオブジェクトを返すようにすることは、一般的には悪い考えだと思います。私はその場合、2つの別々の方法を扱うことを好むでしょう。 – zeekay

+0

あなたは正しいです、それは*悪いです。それはちょうどそれが今や魅力的な便利なものです。 – Owen

1

は、それが所望のタイプ(あなたのケースでstr)から継承するオブジェクトを許可しないと同じように、より良い方法はisinstance()を使用することで、本当に悪い習慣です:

if isinstance(my_var, str): 
    my_code_here() 

また、これを行うためのpythonicの方法は、あなたが言及したようにダックタイピングであるのですが、なぜコードをtry/exceptブロックに入れるだけですか?したがって、値がの場合はと予想されていない場合にのみ例外がキャッチされます(ダックのように突っ込んで歩くと、それはアヒルです)。

0
class Node(object): 
    def __new__(cls, contents): 
    return contents if isinstance(contents, cls) else object.__new__(cls) 
    def __init__(self, contents): 
    # construct from string... 

class Manager(object): 
    def do_something(self, runner, *args): 
    do_something_else(Node(runner(*args))) 

今それはdoesnのランナーがNodeまたは文字列を返す場合は問題ありません。

関連する問題