2011-07-13 11 views
1

私はすべてのノードが何らかのプロトコルに従う分散システムをシミュレートしています。これには、プロトコルの小さなバリエーションの評価も含まれます。変形とは、単一の方法の代替実施を意味する。 すべてノードは常に同じパターンに従います。パターンは実験設定によって決まります(ある時点で1つの設定だけが有効です)。パフォーマンスを犠牲にすることなく、最も明確な方法は何ですか?Pythonでクラスを設定する最もエレガントな方法は?

実験が非常に広範囲になる可能性があるため、私は明らかに条件を必要としません。前に私は同じように、継承を使用しました:

class Node(object): 

    def dumb_method(self, argument): 
     # ...  

    def slow_method(self, argument): 
     # ... 

    # A lot more methods 

class SmarterNode(Node): 

    def dumb_method(self, argument): 
     # A somewhat smarter variant ... 

class FasterNode(SmarterNode): 

    def slow_method(self, argument): 
     # A faster variant ... 

しかし、今、私はすべての可能な変形にをテストする必要があるとソースを乱雑クラスの指数関数的な数を望んでいません。私はまた、他の人々が最小限の労力でコードを理解して理解してもらいたい。あなたの提案は何ですか?

編集:私が十分強調していなかったことの1つ:想定されているすべてのユースケースについて、設定時にクラスをパッチするのは良いと思われます。つまり、単純に作業することができます。Node.dumb_method = smart_methodしかし、どういうわけか、という感じがしませんでした。この種の解決策は、ランダムなスマートリーダーに重大な頭痛を引き起こすでしょうか?あなたはこれに似た何かをしようとしている場合、私はわからない

+0

メソッドのリストを維持しますか?私が見る限り、専用のクラスを必要とするものはありません。戦略パターンを見てみましょう。 – phant0m

+0

はい、多かれ少なかれ*戦略*があります。しかし、ここでは、いつでも、各クライアントがまったく同じ戦略を使用するように、より制約されています。したがって、アルゴリズムをカプセル化するオブジェクトは冗長であり、リソースの無駄を避けたいと考えています。 – lRem

+0

しかし、あなたはすでにアルゴリズムを "カプセル化"しているオブジェクトを持っています:クラス。各クラスはオブジェクトのインスタンスであることを覚えておいてください(デフォルトでは 'type'でメタクラスです) – phant0m

答えて

2

type機能で新しいサブタイプを作成できます。サブクラスの名前空間をdictとして与えるだけです。

# these are supposed to overwrite methods 
def foo(self): 
    return "foo" 

def bar(self): 
    return "bar" 


def variants(base, methods): 
    """ 
     given a base class and list of dicts like [{ foo = <function foo> }] 
     returns types T(base) where foo was overwritten 
    """ 
    for d in methods: 
     yield type('NodeVariant', (base,), d) 


from itertools import combinations 
def subdicts(**fulldict): 
    """ returns all dicts that are subsets of `fulldict` """ 
    items = fulldict.items() 
    for i in range(len(items)+1): 
     for subset in combinations(items, i): 
      yield dict(subset) 

# a list of method variants 
combos = subdicts(slow_method=foo, dumb_method=bar) 

# base class 
class Node(object): 
     def dumb_method(self): 
      return "dumb" 
     def slow_method(self): 
      return "slow" 

# use the base and our variants to make a number of types 
types = variants(Node, combos) 

# instantiate each type and call boths methods on it for demonstration 
print [(var.dumb_method(), var.slow_method()) for var 
      in (cls() for cls in types)] 

# [('dumb', 'slow'), ('dumb', 'foo'), ('bar', 'slow'), ('bar', 'foo')] 
+0

かなりいいです。今私はこれを考えている、または*戦略パターン*を実装しています。 – lRem

0

(スワップアウトランタイム「継承」を可能にする):

class Node(object): 
    __methnames = ('method','method1')  

    def __init__(self, type): 
     for i in self.__methnames: 
      setattr(self, i, getattr(self, i+"_"+type)) 

    def dumb_method(self, argument): 
     # ...  

    def slow_method(self, argument): 
     # ... 

n = Node('dumb') 
n.method() # calls dumb_method 

n = Node('slow') 
n.method() # calls slow_method 

それとも、このような何かを探しているなら(可能に)クラスのすべてのメソッドの実行中の(したがってテスト):

class Node(object): 
    #do something 

class NodeTest(Node): 

    def run_tests(self, ending = ''): 
     for i in dir(self): 
      if(i.endswith(ending)): 
       meth = getattr(self, i) 
       if(callable(meth)): 
        meth() #needs some default args. 
        # or yield meth if you can 
+0

最初のものとよく似ています。しかし、すべての*バリアントは独立しているので、可能なバリエーションごとに1つの '__init__'引数が必要です。私が検討していた1つのオプション:ノードをデフォルトバリアントのセットで単純なものに保つ。設定が終わったら、 'Node.dumb_method = smart_method'でパッチを張ってください。しかし、 'smart_method'はどこに住んでいますか?引数として 'self'を持つ関数ですか?それはインスタンス化されない自分のクラスです。 – lRem

+0

@IRem: 'Node'にすべてのメソッドを渡します(+単に' method'などと呼ばれます)、 'Node.method = None.smart_method'を実行しますか? – Steven

2

あなたは__slots__機構と、ファクトリクラスを使用することができます。それぞれの実験でNodeFactoryをインスタンス化する必要がありますが、そこからNodeインスタンスを作成することになります。例:

class Node(object): 
    __slots__ = ["slow","dumb"] 

class NodeFactory(object): 
    def __init__(self, slow_method, dumb_method): 
     self.slow = slow_method 
     self.dumb = dumb_method 
    def makenode(self): 
     n = Node() 
     n.dumb = self.dumb 
     n.slow = self.slow 
     return n 

の例では、実行します。

>>> def foo(): 
...  print "foo" 
... 
>>> def bar(): 
...  print "bar" 
... 
>>> nf = NodeFactory(foo, bar) 
>>> n = nf.makenode() 
>>> n.dumb() 
bar 
>>> n.slow() 
foo 
+0

素晴らしいメカニズムである '__slots__'に感謝します。しかし、依然としてクラスに直接割り当てても、この場合は十分であり、さらに効率的です。 – lRem

0

あなたはこのためにmetaclassを使用することができます。

場合によっては、バリエーションごとにメソッド名でその場でクラスを作成できます。

+0

これはおそらく私が見たメタクラスの最良の説明です。私はアップヴォートすることができればいいと思う;)私はそれを少し修正した:ピアレビューをしてください。 – lRem

+0

ありがとうございます。実際には他のタイプミスがありますが、私は今それらを修正するにはあまりにも怠惰です:-) –

0

クラスがインスタンス化されたときまたは後で呼び出されるメソッドを決定する必要がありますか?クラスがインスタンス化されたと仮定すると、次のようになります。

class Node(): 
    def Fast(self): 
     print "Fast" 

    def Slow(self): 
     print "Slow" 

class NodeFactory(): 
    def __init__(self, method): 
     self.method = method 

    def SetMethod(self, method): 
     self.method = method 

    def New(self): 
     n = Node() 
     n.Run = getattr(n, self.method) 
     return n 

nf = NodeFactory("Fast") 

nf.New().Run() 
# Prints "Fast" 

nf.SetMethod("Slow") 

nf.New().Run() 
# Prints "Slow" 
+0

私が質問に追加したように、私はさらに、オブジェクトだけでなくクラス全体にパッチを当てることができます。呼び出されるメソッドは、ノードのインスタンス化前の構成時に決定され、ノードが必要な場合は変更されません。 – lRem

+0

私の答えはあなたがしなければならないことをしていないのですか?クラス全体にパッチを当てる必要がある場合を除き、あなたはそうは思わない。 – combatdave

+0

それはありますが、私がそれを考えると、クラスにパッチを当てる方がより有益です。第一に、それは工場の必要性を導入しない。第2に、この特定のケースでは、クラスにパッチを当てることにより、*すべてのノードが常に同じバリエーション*の部分に従うことが保証されます。 – lRem

関連する問題