I have a class for making sprites flyweight and I am using a decorator to call this class. Here is some code:
class flyweight:
def __init__(self, cls):
self._cls = cls
self.__instances = dict()
def __call__(self, title):
return self.__instances.setdefault((title), self._cls(title))
In this question I'll just simplify the code to show what is relevant.
@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))
But this gives me the error:
AttributeError: 'flyweight' object has no attribute 'from_colour'
Should I remodel my flyweight implementation or is there some way around this?
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.