Ошибка при использовании статического метода с навесным декоратором

1

У меня есть класс для создания спрайтов мухи, и я использую декоратор для вызова этого класса. Вот какой код:

class flyweight:
    def __init__(self, cls):
        self._cls = cls
        self.__instances = dict()

    def __call__(self, title):
        return self.__instances.setdefault((title), self._cls(title))

В этом вопросе я просто упрощу код, чтобы показать, что актуально.

@flyweight
class Sprite:
    def __init__(self, title, surf=None):
        self.title = title
        self.surf = surf if surf is not None else pygame.image.load('Images/Sprites/'+title+'.png').convert_alpha()
        self.w, self.h = self.surf.get_size()

    @staticmethod
    def from_colour(colour, size=(40,40)):
        surf = pygame.Surface(size).convert(); surf.fill(colour)
        return Sprite(colour, surf)

red = Sprite.from_colour((125,0,0))

Но это дает мне ошибку:

AttributeError: 'flyweight' object has no attribute 'from_colour'

Должен ли я перестроить мою реализацию мухи или есть какой-то путь вокруг этого?

Теги:
python-3.x
decorator
python-decorators
flyweight-pattern

2 ответа

0
Лучший ответ

flyweight декоратор действительно должен пройти через все конструкторы к базовому классу, включая @classmethod и @staticmethod альтернативные конструкторы. Фактически, в более общем плане, декоратор класса действительно должен сохранить обернутый класс для всего публичного интерфейса.

И, хотя мы могли бы легко модифицировать flyweight чтобы пройти через остальную часть интерфейса Sprite, что в данном случае является именно этим методом from_colour, это будет болью для менее тривиального класса или для класса, который когда-либо изменяется. И действительно, какой смысл делать декоратор, который работает только с одним классом?

Итак, позвольте изменить его на:

  • Возьмите любую подпись конструктора. В идеале мы хотели бы настроить его на то, какая часть подписи подсчитывается как ключ 1, но чтобы вещи не усложнялись, просто исправьте это как первый аргумент.

  • Пройдите через весь открытый интерфейс класса, а не только его интерфейс __call__.

Так:

class flyweight:
    def __init__(self, cls):
        self._cls = cls
        self.__instances = dict()

    def __call__(self, key, *args, **kw):
        return self.__instances.setdefault(key, self._cls(key, *args, **kw))

    def __getattr__(self, name):
        if not name.startswith('_'):
            return getattr(self._cls, name)

1. В какой-то другой библиотеке, которую я использовал, для этого есть приятный дизайн для кэшей памяти функций.Наверное, cachetools.И это должно быть так же важно для классовых кладов.

  • 0
    Отлично. Спасибо!
1

После оформления имя обернутого объекта автоматически указывает на возвращаемые результаты декоратора. В этом случае Sprite теперь хранит экземпляр flyweight, который по очереди содержит атрибут, хранящий экземпляр исходного упакованного класса Sprite. Например, печать Sprite после объявлений дает: <__main__.flyweight object at 0x102373080>. Однако staticmethod from_colour можно вызвать из _cls:

red = Sprite._cls.from_colour((125,0,0))
  • 0
    Хотя это работает, я не думаю, что это отличный ответ. Очевидно, что from_colour должен был быть частью публичного интерфейса Sprite . Но вместо этого теперь вы должны вызывать его через закрытый атрибут. Хуже того, приватный атрибут класса, о котором пользователи Sprite не должны знать. Тем более, что вы должны иметь возможность @flyweight и возвращать его обратно, не изменяя ничего, кроме производительности, и вы определенно не сможете, если это затеняет интерфейс украшенного класса.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню