3

背景: トランザクションの請求先の国に基づいてフローを分割するトランザクションのシステムがあります。2 つのインスタンスに存在するロギング テーブルがあります。1 つの DB は EU にトランザクションを記録し、もう 1 つは他の場所にトランザクションを記録します。大まかに言えば、各テーブルがクラスによって表される DB を操作する根性を管理および非表示にするテスト ライブラリもあります。テーブルを表すクラスがあり、db セッション マネージャー クラスには、クラスの 2 つのインスタンスのそれぞれに対して 2 つのメンバーがあります。私がやりたいことは、任意の呼び出しを受け取り、引数を検査し、入力引数の 1 つに基づいて、呼び出しを正しい db インスタンスを表すクラス インスタンスにディスパッチする汎用の「メタ ダオ」クラスを作成することです。最初はすべてのメソッドをオーバーロードすることだけを考えていましたが、それは'

__getattr__メソッドルックアップをオーバーライドして、受信したメソッドの名前に基づいて正しいインスタンスを呼び出すことができるようにすることを検討していまし__getattr__たが、私が理解していることinspectから、内から着信メソッド引数を取得できない__getattr__ため、この場合、その中から適切にディスパッチしないでください。私が追求できる別の方向性、またはメソッド名だけでなく、内部から引数を「検査」する方法についてのアイデアはあります__getattr__か?

[編集] これが私が話していることの一般化されたバージョンです:

class BarBase(object):
    def __init__(self, x):
        self.x = x
    def do_bar(self, i):
        return self.x * i

class FooBar(BarBase):
    def __init__(self, x):
        super(FooBar, self).__init__(x)
    def do_foo(self, i):
        return self.x + i

class MetaFoo(object):
    def __init__(self, bar_manager):
        self.foo_manager = bar_manager
    #something here that will take an arbitrary methodname and args as
    #long as args includes a value named i, inspect i, and call
    #bar_manager.fooa.[methodname](args) if i < 10,
    #and bar_manager.foob.[methodname](args) if i >= 10

class BarManager(object):
    def __init__(self):
        self.bar_list = {}
    def __get_fooa(self):
        if 'fooa' not in self.bar_list.keys():
            self.bar_list['fooa'] = FooBar('a')
        return self.bar_list['fooa']
    fooa = property(__get_fooa)
    def __get_foob(self):
        if 'foob' not in self.bar_list.keys():
            self.bar_list['foob'] = FooBar('b')
        return self.bar_list['foob']
    foob = property(__get_foob)
    def __get_foo(self):
        if 'foo' not in self.bar_list.keys():
            self.bar_list['foo'] = MetaFoo(self)
        return self.bar_list['foo']
4

3 に答える 3

2

これらの線に沿った何かが機能するはずです:

class ProxyCall(object):
   '''Class implementing the dispatch for a certain method call'''
   def __init__(self, proxy, methodname):
      self.proxy = proxy
      self.methodname = methodname

   def __call__(self, *p, **kw):
      if p[0] == "EU": # or however you determine the destination
         return getattr(self.proxy.EU, self.methodname)(*p, **kw);
      else:
         return getattr(self.proxy.OTHER, self.methodname)(*p, **kw);


class Proxy(object):
   '''Class managing the different "equivalent" instances'''
   def __init__(self, EU, OTHER):
      self.EU = EU
      self.OTHER = OTHER

   def __getattr__(self, name):
      if not hasattr(self.EU, name):
         # no such method
         raise AttributeError()
      else:
         # return object that supports __call__ and will make the dispatch
         return ProxyCall(self, name)

次に、2つのインスタンスを作成し、それらをプロキシオブジェクトに結合します。

eu = make_instance(...)
other = make_instance(...)
p = Proxy(eu, other)
p.somemethod(foo) 
于 2011-12-14T19:28:56.133 に答える
2

python デコレータはあなたの友達です。このようなことができます

class MetaFoo(object):

    def overload(func):
        """
        we need to check a named variable so for simplicity just checking kwargs
        """
        def _wrapper(*args, **kwargs):
            if kwargs.get('i',0) < 10:
                # get func.func_name from foo and call it
                print "calling foo.",func.func_name
            else:
                print "calling bar.",func.func_name

            func(*args, **kwargs)

        return _wrapper

    @overload
    def func1(self, i):
        print "default functionality"


MetaFoo().func1(i=5)
MetaFoo().func1(i=10)

出力:

calling foo. func1
default functionality
calling bar. func1
default functionality

オーバーライドするメソッドがほとんどない場合は、デコレータを個別に適用し、差分しきい値などのパラメーターをさまざまなメソッドに渡すこともできますが、すべてのメソッドをオーバーライドする場合は、特定のクラスのすべてのメソッドをオーバーロードするメタクラスを追加できますが、この場合__getattr__で提案されているオーバーライドsthは良い代替手段です

于 2011-12-14T19:34:00.640 に答える
0

渡された引数に基づくディスパッチは、2 段階のプロセスです。

  1. __getattr__プロキシ メソッドを返します
  2. python はプロキシを呼び出し、どの実際のメソッドを呼び出すかを決定します

次に例を示します。

from functools import partial

class TwoFold(object):
    EU = ('GERMANY','FRANCE','ITALY','GREECE',)
    def __getattr__(self, name):
        try:
            EU = object.__getattribute__(self, 'EU_' + name)
            Other = object.__getattribute__(self, 'Other_' + name)
        except AttributeError:
            raise AttributeError(
                "%r is missing an EU_%s or Other_%s" % (self, name, name)
                )
        judge = partial(self._judge, name, EU, Other)
        return judge
    def _judge(self, method_name, EU, Other, *args, **kwargs):
        if kwargs['country'].upper() in self.EU:
            method = EU
        else:
            method = Other
        return method(*args, **kwargs)
    def EU_log(self, tax, country):
        print "logging EU access for %s, tax rate of %r" % (country, tax)
    def Other_log(self, tax, country):
        print "logging non-EU access for %s, tax rate of %r" % (country, tax)

if __name__ == '__main__':
    test = TwoFold()
    test.log(7.5, country='France')
    test.log(10.1, country='Greece')
    test.log(8.9, country='Brazil')
    test.howsat('blah')

実行すると、次のようになります。

logging EU access for France, tax rate of 7.5
logging EU access for Greece, tax rate of 10.1
logging non-EU access for Brazil, tax rate of 8.9

に続く:

Traceback (most recent call last):
  File "test.py", line 29, in <module>
    test.howsat('blah')
  File "test.py", line 10, in __getattr__
raise AttributeError("%r is missing an EU_%s or Other_%s" % (self, name, name))
AttributeError: <__main__.TwoFold object at 0x00B4A970> is missing an
    EU_howsat or Other_howsat

これを機能させるには、常に同じキーワード引数を使用する (関数を呼び出すときに名前を付ける) か、引数を常に同じ位置に配置する必要があります。または、スタイル/カテゴリ/メソッドの種類ごとにいくつかの異なるプロキシを作成できます。

于 2012-02-29T23:29:23.447 に答える