2

さまざまなクラスのメソッドをグローバルレジストリに配置するために、メタクラスを持つデコレータを使用しています。デコレータタグ、メタクラスは関数をレジストリに配置します。

class ExposedMethod (object):
    def __init__(self, decoratedFunction):
        self._decoratedFunction = decoratedFunction

    def __call__(__self,*__args,**__kw):
        return __self._decoratedFunction(*__args,**__kw)

class ExposedMethodDecoratorMetaclass(type):
    def __new__(mcs, name, bases, dct):
        for obj_name, obj in dct.iteritems():
            if isinstance(obj, ExposedMethod):
                WorkerFunctionRegistry.addWorkerToWorkerFunction(obj_name, name)
        return type.__new__(mcs, name, bases, dct)

class MyClass (object):
    __metaclass__ = DiscoveryExposedMethodDecoratorMetaclass

    @ExposeDiscoveryMethod
    def myCoolExposedMethod (self):
        pass

これで、2つの関数レジストリが必要になるようになりました。最初に考えたのは、メタクラスをサブクラス化し、他のレジストリを配置することでした。そのためには、新しいメソッドを単に書き直す必要があります。

書き換えは冗長なコードを意味するので、これは私が本当に望んでいることではありません。したがって、 newが実行されたときに読み取ることができるメタクラス内に属性を配置する方法を誰かが指定できると便利です。これにより、新しいを書き直すことなく、適切なレジストリを配置できます。

4

3 に答える 3

5

Your ExposedMethod instances do not behave as normal instance methods but rather like static methods -- the fact that you're giving one of them a self argument hints that you're not aware of that. You may need to add a __get__ method to the ExposedMethod class to make it a descriptor, just like function objects are -- see here for more on descriptors.

But there is a much simpler way, since functions can have attributes...:

def ExposedMethod(registry=None):
    def decorate(f):
        f.registry = registry
        return f
    return decorate

and in a class decorator (simpler than a metaclass! requires Python 2.6 or better -- in 2.5 or earlier you'll need to stick w/the metaclass or explicitly call this after the class statement, though the first part of the answer and the functionality of the code below are still perfectly fine):

def RegisterExposedMethods(cls):
    for name, f in vars(cls).iteritems():
        if not hasattr(f, 'registry'): continue
        registry = f.registry
        if registry is None:
            registry = cls.registry
        registry.register(name, cls.__name__)
    return cls

So you can do:

@RegisterExposedMethods
class MyClass (object):

    @ExposeMethod(WorkerFunctionRegistry)
    def myCoolExposedMethod (self):
        pass

and the like. This is easily extended to allowing an exposed method to have several registries, get the default registry elsewhere than from the class (it could be in the class decorator, for example, if that works better for you) and avoids getting enmeshed with metaclasses without losing any functionality. Indeed that's exactly why class decorators were introduced in Python 2.6: they can take the place of 90% or so of practical uses of metaclasses and are much simpler than custom metaclasses.

于 2010-04-30T15:35:51.897 に答える
0

お二方、ご回答ありがとうございます。どちらも、私の要求に適切な方法を見つけるのに大いに役立ちました。

問題に対する私の最終的な解決策は次のとおりです。

def ExposedMethod(decoratedFunction):
    decoratedFunction.isExposed = True
    return decoratedFunction

class RegisterExposedMethods (object):
    def __init__(self, decoratedClass, registry):
        self._decoratedClass = decoratedClass
        for name, f in vars(self._decoratedClass).iteritems():
            if hasattr(f, "isExposed"):
                registry.addComponentClassToComponentFunction(name, self._decoratedClass.__name__)

        # cloak us as the original class
        self.__class__.__name__ = decoratedClass.__name__

    def __call__(self,*__args,**__kw):
        return self._decoratedClass(*__args,**__kw)

    def __getattr__(self, name):
        return getattr(self._decoratedClass, name)

メソッドを公開したいクラスで、次のことを行います。

@RegisterExposedMethods
class MyClass (object):
    @ExposedMethod
    def myCoolExposedMethod (self):
        pass

クラス デコレータのサブクラス化が非常に簡単になりました。次に例を示します。

class DiscoveryRegisterExposedMethods (RegisterExposedMethods):
    def __init__(self, decoratedClass):
        RegisterExposedMethods.__init__(self,
                                        decoratedClass,
                                        DiscoveryFunctionRegistry())

ということでアレックスのコメント

ExposedMethod インスタンスは、通常のインスタンス メソッドとして動作しません ...

メソッドは単にタグ付けされ、ラップされていないためです。

于 2010-05-02T20:53:40.747 に答える
0

class 属性を使用して、特殊なメタクラスで使用するレジストリを指すことができます。たとえば、次のようになります。

class ExposedMethodDecoratorMetaclassBase(type):

    registry = None

    def __new__(mcs, name, bases, dct):
        for obj_name, obj in dct.items():
            if isinstance(obj, ExposedMethod):
                mcs.registry.register(obj_name, name)
        return type.__new__(mcs, name, bases, dct)


class WorkerExposedMethodDecoratorMetaclass(ExposedMethodDecoratorMetaclassBase):

    registry = WorkerFunctionRegistry


class RetiredExposedMethodDecoratorMetaclass(ExposedMethodDecoratorMetaclassBase):

    registry = RetiredFunctionRegistry
于 2010-04-30T13:19:13.973 に答える