9.5. Flyweight¶
9.5.1. Rationale¶
EN: Flyweight
PL: Pyłek
Type: object
9.5.2. Use Cases¶
In applications with large number of objects
Objects take significant amount of memory
Reduce memory consumed by objects
9.5.3. Problem¶
Imagine mapping application, such as: Open Street Maps, Google Maps, Yelp, Trip Advisor etc.
There are thousands points of interests such as Cafe, Shops, Restaurants, School etc.
Icons can take a lot of memory, times number of points on the map
It might crash with out of memory error (especially on mobile devices)
from dataclasses import dataclass
from enum import Enum
class PointType(Enum):
HOSPITAL = 1
CAFE = 2
RESTAURANT = 3
@dataclass
class Point:
__x: int # 28 bytes
__y: int # 28 bytes
__type: PointType # 1064 bytes
__icon: bytearray # empty: 56 bytes, but with PNG icon: 20 KB
def draw(self) -> None:
print(f'{self.__type} at ({self.__x}, {self.__y})')
class PointService:
def get_points(self) -> list[Point]:
points: list[Point] = list()
point: Point = Point(1, 2, PointType.CAFE, None)
points.append(point)
return points
if __name__ == '__main__':
service = PointService()
for point in service.get_points():
point.draw()
# PointType.CAFE at (1, 2)
9.5.4. Design¶

9.5.5. Implementation¶
Separate the data you want to share from other data
Pattern will create a dict with point type and its icon
It will reuse icon for each type
So it will prevent from storing duplicated data in memory

from dataclasses import dataclass, field
from enum import Enum
from typing import Final
class PointType(Enum):
HOSPITAL = 1
CAFE = 2
RESTAURANT = 3
@dataclass
class PointIcon:
__type: Final[PointType] # 1064 bytes
__icon: Final[bytearray] # empty: 56 bytes, but with PNG icon: 20 KB
def get_type(self):
return self.__type
@dataclass
class PointIconFactory:
__icons: dict[PointType, PointIcon] = field(default_factory=dict)
def get_point_icon(self, type: PointType) -> PointIcon:
if not self.__icons.get(type):
self.__icons[type] = PointIcon(type, None) # Here read icon from filesystem
return self.__icons.get(type)
@dataclass
class Point:
__x: int # 28 bytes
__y: int # 28 bytes
__icon: PointIcon
def draw(self) -> None:
print(f'{self.__icon.get_type()} at ({self.__x}, {self.__y})')
@dataclass
class PointService:
__icon_factory: PointIconFactory
def get_points(self) -> list[Point]:
points: list[Point] = list()
point: Point = Point(1, 2, self.__icon_factory.get_point_icon(PointType.CAFE))
points.append(point)
return points
if __name__ == '__main__':
service = PointService(PointIconFactory())
for point in service.get_points():
point.draw()
# PointType.CAFE at (1, 2)