Создание декоратора в классе с доступом к самому (текущему) классу - PullRequest
0 голосов
/ 15 июля 2009

В настоящее время я делаю это следующим образом:

class Spam(object):

    decorated = None

    @classmethod
    def decorate(cls, funct):
        if cls.decorated is None:
            cls.decorated = []
        cls.decorated.append(funct)
        return funct


class Eggs(Spam):
    pass


@Eggs.decorate
def foo():
    print "spam and eggs"


print Eggs.decorated # [<function foo at 0x...>]
print Spam.decorated # None

Мне нужно , чтобы иметь возможность сделать это в подклассе, как показано. Проблема в том, что я не могу понять, как сделать поле decorated недоступным для всех экземпляров. Прямо сейчас у меня есть хакерское решение, изначально установив его на None, а затем проверив, когда функция оформлена, но это работает только в одном направлении. Другими словами, если я подкласс Eggs, а затем украсить что-то с помощью функции Eggs.decorate, это влияет на все подклассы.

Я предполагаю, что мой вопрос: возможно ли иметь изменяемые поля классов, которые не будут разделены между базовыми и подклассами?

Ответы [ 3 ]

1 голос
/ 15 июля 2009

Я понял это с помощью метаклассов. Спасибо всем, кто написал. Вот мое решение, если кто-нибудь сталкивался с подобной проблемой:

class SpamMeta(type):

    def __new__(cls, name, bases, dct):
        SpamType = type.__new__(cls, name, bases, dct)
        SpamType.decorated = []
        return SpamType


class Spam(object):

    __metaclass__ = SpamMeta

    @classmethod
    def decorate(cls, funct):
        cls.decorated.append(funct)
        return funct


class Eggs(Spam):
    pass


@Eggs.decorate
def foo():
    print "spam and eggs"


print Eggs.decorated # [<function foo at 0x...>]
print Spam.decorated # []
0 голосов
/ 16 июля 2009

Не то чтобы я имел что-то против метаклассов, но вы также можете решить это без них:

from collections import defaultdict

class Spam(object):
    _decorated = defaultdict(list)

    @classmethod
    def decorate(cls, func):
        cls._decorated[cls].append(func)
        return func

    @classmethod
    def decorated(cls):
        return cls._decorated[cls]


class Eggs(Spam):
    pass

@Eggs.decorate
def foo():
    print "spam and eggs"

print Eggs.decorated() # [<function foo at 0x...>]
print Spam.decorated() # []

Невозможно иметь свойства объектов класса (если вы снова не вернетесь к метаклассам), поэтому обязательно снова получить список декорированных методов с помощью метода класса. Существует дополнительный уровень косвенности по сравнению с решением метакласса.

0 голосов
/ 15 июля 2009

Я вполне уверен, что вы не можете. Я думал об этом с помощью property (), но, к сожалению, класс самого класса - куда нужно будет добавить свойство - это сам ClassType.

Вы можете написать свой декоратор так, но он немного меняет интерфейс:

class Spam(object):
    decorated = {}

    @classmethod
    def get_decorated_methods(cls):
        return cls.decorated.setdefault(cls, [])

    @classmethod
    def decorate(cls, funct):
        cls.get_decorated_methods().append(funct)
        return funct


class Eggs(Spam):
    pass


@Spam.decorate
def foo_and_spam():
    print "spam"

@Eggs.decorate
def foo_and_eggs():
    print "eggs"

print Eggs.get_decorated_methods() # [<function foo_and_eggs at 0x...>]
print Spam.get_decorated_methods() # [<function foo_and_spam at 0x...>]
...