Python: подкласс метакласса - PullRequest
1 голос
/ 30 апреля 2010

Для помещения методов различных классов в глобальный реестр я использую декоратор с метаклассом. Теги декоратора, метакласс помещает функцию в реестр:

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 .

Поскольку переписывание означает избыточный код, это не то, чего я действительно хочу. Поэтому было бы неплохо, если бы кто-нибудь мог назвать способ поместить атрибут внутри метакласса, который может быть прочитан при выполнении new . При этом можно было бы ввести правильный реестр без необходимости переписывать new .

Ответы [ 3 ]

5 голосов
/ 30 апреля 2010

Ваши ExposedMethod экземпляры ведут себя не как обычные методы экземпляров, а как статические методы - тот факт, что вы даете одному из них аргумент self, указывает на то, что вы этого не знаете.Возможно, вам понадобится добавить метод __get__ в класс ExposedMethod, чтобы сделать его дескриптором, точно так же как объекты функций - см. здесь для получения дополнительной информации о дескрипторах.

Но естьэто намного более простой способ, поскольку функции могут иметь атрибуты ...:

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

и в декораторе class (проще, чем метакласс! требуется 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% практического использования метаклассов и на намного проще, чем пользовательские метаклассы.

0 голосов
/ 03 мая 2010

Спасибо вам обоим за ваши ответы. Оба помогли найти правильный путь для моей просьбы.

Мое окончательное решение проблемы:

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 не ведут себя как обычные методы экземпляров ...

больше не соответствует действительности, поскольку метод просто помечен и не упакован.

0 голосов
/ 30 апреля 2010

Вы можете использовать атрибут класса, чтобы указать реестр, который вы хотите использовать в специализированных метаклассах, например, :

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
...