#!/usr/bin/env python # -*- coding: utf-8 -*- """http://codesnipers.com/?q=python-flyweights""" import weakref class FlyweightMeta(type): def __new__(mcs, name, parents, dct): """ :param name: class name :param parents: class parents :param dct: dict: includes class attributes, class methods, static methods, etc :return: new class """ # set up instances pool dct['pool'] = weakref.WeakValueDictionary() return super(FlyweightMeta, mcs).__new__(mcs, name, parents, dct) @staticmethod def _serialize_params(cls, *args, **kwargs): """Serialize input parameters to a key. Simple implementation is just to serialize it as a string """ args_list = map(str, args) args_list.extend([str(kwargs), cls.__name__]) key = ''.join(args_list) return key def __call__(cls, *args, **kwargs): key = FlyweightMeta._serialize_params(cls, *args, **kwargs) pool = getattr(cls, 'pool', {}) instance = pool.get(key) if not instance: instance = super(FlyweightMeta, cls).__call__(*args, **kwargs) pool[key] = instance return instance class Card(object): """The object pool. Has builtin reference counting""" _CardPool = weakref.WeakValueDictionary() """Flyweight implementation. If the object exists in the pool just return it (instead of creating a new one)""" def __new__(cls, value, suit): obj = Card._CardPool.get(value + suit) if not obj: obj = object.__new__(cls) Card._CardPool[value + suit] = obj obj.value, obj.suit = value, suit return obj # def __init__(self, value, suit): # self.value, self.suit = value, suit def __repr__(self): return "" % (self.value, self.suit) class Card2(object): __metaclass__ = FlyweightMeta def __init__(self, *args, **kwargs): # print('Init {}: {}'.format(self.__class__, (args, kwargs))) pass if __name__ == '__main__': import sys if sys.version_info[0] > 2: sys.stderr.write("!!! This example is compatible only with Python 2 ATM !!!\n") raise SystemExit(0) # comment __new__ and uncomment __init__ to see the difference c1 = Card('9', 'h') c2 = Card('9', 'h') print(c1, c2) print(c1 == c2, c1 is c2) print(id(c1), id(c2)) c1.temp = None c3 = Card('9', 'h') print(hasattr(c3, 'temp')) c1 = c2 = c3 = None c3 = Card('9', 'h') print(hasattr(c3, 'temp')) # Tests with metaclass instances_pool = getattr(Card2, 'pool') cm1 = Card2('10', 'h', a=1) cm2 = Card2('10', 'h', a=1) cm3 = Card2('10', 'h', a=2) assert (cm1 == cm2) != cm3 assert (cm1 is cm2) is not cm3 assert len(instances_pool) == 2 del cm1 assert len(instances_pool) == 2 del cm2 assert len(instances_pool) == 1 del cm3 assert len(instances_pool) == 0 ### OUTPUT ### # (, ) # (True, True) # (31903856, 31903856) # True # False