StructuralPythonverifiedVerified
Flyweight Pattern in Python
Minimizes memory usage by sharing fine-grained objects that represent repeated data, storing intrinsic state once and passing extrinsic state at call time.
How to Implement the Flyweight Pattern in Python
1Step 1: Define the shared intrinsic state
from dataclasses import dataclass
@dataclass(frozen=True)
class CharacterStyle:
font: str
size: int
bold: bool2Step 2: Create the Flyweight that holds shared state
@dataclass(frozen=True)
class Character:
char: str
style: CharacterStyle
def render(self, x: int, y: int) -> None:
bold = " bold" if self.style.bold else ""
print(f'"{self.char}" [{self.style.font} {self.style.size}px{bold}] @ ({x},{y})')3Step 3: Build a factory that pools and reuses flyweights
class CharacterFactory:
def __init__(self) -> None:
self._pool: dict[str, Character] = {}
def get(self, char: str, style: CharacterStyle) -> Character:
key = f"{char}|{style.font}|{style.size}|{style.bold}"
if key not in self._pool:
self._pool[key] = Character(char, style)
print(f'[Factory] created flyweight for key "{key}"')
return self._pool[key]
@property
def pool_size(self) -> int:
return len(self._pool)4Step 4: Render text and verify flyweight reuse
factory = CharacterFactory()
style = CharacterStyle("Arial", 12, False)
# Rendering "ABBA" -- "A" and "B" flyweights are reused
for i, ch in enumerate("ABBA"):
factory.get(ch, style).render(i * 10, 0)
print("Flyweights in pool:", factory.pool_size) # 2, not 4"""Game entity system using Flyweight for shared sprite data."""
import logging
from dataclasses import dataclass, field
from typing import Any
logger = logging.getLogger(__name__)
# [step] Define intrinsic (shared) and extrinsic (per-instance) state
@dataclass(frozen=True)
class EntityType:
kind: str
texture_url: str
mesh_data: bytes # Large shared data
base_stats: dict[str, int]
@dataclass
class EntityState:
x: float
y: float
health: int
# [step] Implement the Flyweight and its factory
class GameEntity:
__slots__ = ("type",)
def __init__(self, entity_type: EntityType) -> None:
self.type = entity_type
def render(self, state: EntityState) -> None:
print(
f"[{self.type.kind}] pos=({state.x},{state.y}) "
f"hp={state.health} tex={self.type.texture_url}"
)
def update(self, state: EntityState, delta_ms: float) -> EntityState:
speed = self.type.base_stats.get("speed", 0)
return EntityState(
x=state.x + speed * (delta_ms / 1000),
y=state.y,
health=state.health,
)
class EntityFactory:
def __init__(self) -> None:
self._flyweights: dict[str, GameEntity] = {}
def register(self, entity_type: EntityType) -> None:
if entity_type.kind not in self._flyweights:
self._flyweights[entity_type.kind] = GameEntity(entity_type)
logger.info('[EntityFactory] registered flyweight "%s"', entity_type.kind)
def get(self, kind: str) -> GameEntity:
fw = self._flyweights.get(kind)
if fw is None:
raise KeyError(f'Unknown entity kind: "{kind}"')
return fw
@property
def flyweight_count(self) -> int:
return len(self._flyweights)
# [step] Demonstrate flyweight reuse across many instances
factory = EntityFactory()
factory.register(EntityType(
kind="orc",
texture_url="/textures/orc.png",
mesh_data=b"\x00" * 1024,
base_stats={"attack": 15, "defense": 8, "speed": 3},
))
factory.register(EntityType(
kind="elf",
texture_url="/textures/elf.png",
mesh_data=b"\x00" * 1024,
base_stats={"attack": 12, "defense": 6, "speed": 7},
))
entities = [
(factory.get("orc" if i % 2 == 0 else "elf"), EntityState(i * 2, 0, 100))
for i in range(1000)
]
for fw, state in entities[:3]:
fw.render(state)
print("Flyweights used:", factory.flyweight_count) # 2, not 1000Flyweight Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Flyweight Pattern in the Real World
“A book publisher doesn’t print a separate metal typeface block for every letter ‘e’ on every page. Instead, one block for ‘e’ (intrinsic state) is reused in every position, with the printer supplying the ink color and position (extrinsic state) each time it is stamped. Thousands of impressions share one piece of metal.”