1

我有一个制作精灵flyweight的类,我正在使用装饰器来调用这个类。这是一些代码:

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'

我应该改造我的轻量级实现还是有办法解决这个问题?

4

2 回答 2

1

装饰后,被包装对象的名称会自动指向装饰器返回的结果。在这种情况下,Spritenow 存储 的实例flyweight,该实例又包含存储原始包装类实例的属性Sprite。例如,Sprite在声明之后打印会给出:<__main__.flyweight object at 0x102373080>. 但是,staticmethod from_colour可以从以下位置调用_cls

red = Sprite._cls.from_colour((125,0,0))
于 2018-06-22T21:20:29.597 回答
0

A flyweight decorator really ought to pass through all constructors to the underlying class, including @classmethod and @staticmethod alternate constructors. In fact, more generally, a class decorator really ought to preserve the wrapped class's entire public interface.

And, while we could easily modify flyweight to specifically pass through the rest of the Sprite interface, which in this case is just that from_colour method, that would be a pain for a less trivial class, or for a class that ever changes. And really, what's the point of making a decorator that only works with a single class?

So, let's change it to:

  • Take any constructor signature. Ideally we'd want to make it configurable on what part of the signature counts as the key,1 but to keep things from getting too complicated, let's just fix it as the first argument.

  • Pass through the entire public interface of the class, not just its __call__ interface.

So:

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. Some other library I've used has a nice design for this for function memo caches. Probably cachetools. And it ought to make just as much sense for class construction caches.

于 2018-06-23T00:50:47.557 回答