You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于使用Python __init_subclass__注册产品与类的工厂设计模式实现的技术咨询

使用__init_subclass__实现工厂设计模式的方案解析

Great question! Using __init_subclass__ to implement a factory pattern is a clean, Pythonic approach that leverages a feature introduced in PEP 487 to automate subclass registration. Let's break down your example, explore how it works, and share some insights and improvements.

Core Concept

__init_subclass__ is a special method that runs automatically whenever a subclass is defined. This makes it perfect for building a factory: instead of manually registering each subclass with the factory, the registration happens automatically when you create the subclass. This eliminates repetitive boilerplate code and reduces the chance of human error.

Example Breakdown

Let's walk through your code step by step:

Parent Class (Shape)

  • shapes_classes: A class-level dictionary that acts as the registry, mapping product identifiers (like 'triangle') to their corresponding subclass types.
  • __init_subclass__: The magic here—when a subclass (like Triangle) is defined, this method is called on the parent Shape class. It adds the subclass to shapes_classes using the subclass's product attribute as the key.
  • create_shape: The factory method. Given a product identifier, it looks up the corresponding class in the registry and returns a new instance of that class with the provided arguments.
  • Abstract methods like area(): Enforce that all subclasses implement core functionality, ensuring consistency across your product types.

Subclasses (Triangle, Square)

Each subclass only needs to do two things:

  1. Define a unique product identifier (e.g., product = 'triangle')
  2. Implement its own initialization logic and required methods (like area())

No manual registration code is needed—this happens automatically when the subclass is defined.

Usage & Output

Your example shows how straightforward it is to use the factory:

# The registry is automatically populated when subclasses are defined
Shape.shapes_classes  # {'triangle': __main__.Triangle, 'square': __main__.Square}

# Create instances via the factory method
triangle = Shape.create_shape(product='triangle', base=3, height=4)
square = Shape.create_shape(product='square', base=3)

# Use the instances as expected
print(triangle)  # I am Triangle shape
print(square)    # I am Square shape
triangle.area()  # 6.0
square.area()    # 9

Improvements & Best Practices

Your base implementation is solid, but here are a few tweaks to make it more robust:

1. Handle Invalid Product Requests

Right now, requesting a product that's not registered will throw a KeyError. Adding error handling makes the factory more user-friendly:

@classmethod
def create_shape(cls, product, *args, **kwargs):
    try:
        return cls.shapes_classes[product](*args, **kwargs)
    except KeyError:
        raise ValueError(f"No shape registered for product: '{product}'") from None

2. Enforce Required product Attribute

Prevent subclasses from being defined without a product identifier by adding a check in __init_subclass__:

def __init_subclass__(cls, **kwargs):
    super().__init_subclass__(**kwargs)
    if not hasattr(cls, 'product') or cls.product is None:
        raise TypeError(f"Subclass {cls.__name__} must define a non-null 'product' attribute")
    cls.shapes_classes[cls.product] = cls

3. Avoid Accidental Registry Overwrites

If two subclasses use the same product key, the later one will overwrite the earlier entry. Add a check to prevent this:

def __init_subclass__(cls, **kwargs):
    super().__init_subclass__(**kwargs)
    if cls.product in cls.shapes_classes:
        existing_class = cls.shapes_classes[cls.product].__name__
        raise ValueError(f"Product '{cls.product}' is already registered by {existing_class}")
    cls.shapes_classes[cls.product] = cls

Why This Approach Shines

  • Open/Closed Principle: You can add new shapes by creating new subclasses without modifying the Shape factory class itself.
  • Reduced Boilerplate: No manual registration calls (like Shape.register(Triangle))—the code stays clean and focused.
  • Maintainability: Subclasses only need to worry about their own logic, while the factory handles instantiation and registration.

内容的提问来源于stack exchange,提问作者Gena Yekelis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 16:47:37