【问题标题】:Error when using staticmethod with flyweight decorator将静态方法与享元装饰器一起使用时出错
【发布时间】:2018-12-02 09:19:51
【问题描述】:

我有一个用于制作精灵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'

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

【问题讨论】:

    标签: python python-3.x decorator python-decorators flyweight-pattern


    【解决方案1】:

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

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

    【讨论】:

    • 虽然这可行,但我认为这不是一个很好的答案。显然from_colour 应该是Sprite 的公共接口的一部分。但是,您现在必须通过私有属性调用它。更糟糕的是,Sprite 的用户不必知道的类的私有属性。尤其是因为您应该能够将@flyweight 放入并取出它而不改变任何东西,但性能除外,如果它掩盖了装饰类的界面,您肯定不能。
    【解决方案2】:

    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。它应该对类构造缓存同样有意义。

    【讨论】:

    • 完美。谢谢!
    猜你喜欢
    • 2014-03-16
    • 2011-09-18
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 2015-02-21
    • 1970-01-01
    • 2020-02-25
    • 2012-12-17
    相关资源
    最近更新 更多