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

我现在已经到了需要两个函数注册表的地步。第一个想法是对元类进行子类化并将另一个注册表放入其中。为此,只需重写新方法即可。

由于重写意味着冗余代码,这不是我真正想要的。因此,如果有人能说出一种如何将属性放入元类中的方法,该方法可以在执行new时读取,那就太好了。这样就可以放入正确的注册表,而无需重写新的.

4

3 回答 3

5

你的ExposedMethod实例不像普通的实例方法,而是像静态方法——你给其中一个实例一个self参数的事实暗示你没有意识到这一点。您可能需要向类添加一个__get__方法以ExposedMethod使其成为描述符,就像函数对象一样——有关描述符的更多信息,请参见此处

但是有一个简单的方法,因为函数可以具有属性......:

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

并且在装饰器中(比元类更简单!需要 Python 2.6 或更高版本 - 在 2.5 或更早版本中,您需要坚持使用元类或在class语句之后显式调用它,尽管答案的第一部分和功能下面的代码仍然很好):

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

所以你可以这样做:

@RegisterExposedMethods
class MyClass (object):

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

之类的。这很容易扩展到允许公开的方法具有多个注册表,从类之外的其他地方获取默认注册表(例如,它可能在类装饰器中,如果这对您更有效)避免与元类纠缠在一起而不会丢失任何功能。事实上,这正是 Python 2.6 中引入类装饰器的原因:它们可以代替元类的 90% 左右的实际用途,并且比自定义元类简单得多

于 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 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 回答