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

Python中如何为从元类继承的方法生成可被help()识别的文档?

问题背景

先给出初始的元类与业务类定义:

class Meta(type):
    """Python元类定义"""
    def greet_user(cls):
        """打印友好问候,展示当前类名"""
        print(f"Hello, I'm the class '{cls.__name__}'!")


class UsesMeta(metaclass=Meta):
    """使用Meta作为元类的业务类"""

元类中定义的方法会被使用该元类的类继承,可直接通过类调用,控制台运行测试代码效果如下:

>>> UsesMeta.greet_user()
Hello, I'm the class 'UsesMeta'!

存在的缺陷

该方案的重大问题是元类方法的文档不会出现在子类的help输出里。运行help(UsesMeta)的输出如下,完全找不到greet_user方法的引用,更不用说对应的docstring了:

Help on class UsesMeta in module __main__:
class UsesMeta(builtins.object)
 |  A class that uses `Meta` as its metaclass.
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

现有解决方案

因为类的__doc__属性是可写的,所以可以重写元类逻辑,把元类方法的文档手动拼接进子类的__doc__里,实现代码如下:

from pydoc import render_doc
from functools import cache

def get_documentation(func_or_cls):
    """将help函数的输出转换为字符串返回"""
    return '\n'.join(render_doc(func_or_cls).splitlines()[2:])


class Meta(type):
    """Python元类定义"""

    @classmethod
    @cache
    def _docs(metacls) -> str:
        """获取元类中所有公开方法和属性的文档"""

        divider = '\n\n----------------------------------------------\n\n'
        metacls_name = metacls.__name__
        metacls_dict = metacls.__dict__

        methods_header = (
            f'从元类`{metacls_name}`继承的类方法'
            f'\n\n'
        )

        method_docstrings = '\n\n'.join(
            get_documentation(method)
            for method_name, method in metacls_dict.items()
            if not (method_name.startswith('_') or isinstance(method, property))
        )

        properties_header = (
            f'从元类`{metacls_name}`继承的类属性'
            f'\n\n'
        )

        properties_docstrings = '\n\n'.join(
            f'{property_name}\n{get_documentation(prop)}'
            for property_name, prop in metacls_dict.items()
            if isinstance(prop, property) and not property_name.startswith('_')
        )

        return ''.join((
            divider,
            methods_header,
            method_docstrings,
            divider,
            properties_header,
            properties_docstrings,
            divider
        ))


    def __new__(metacls, cls_name, cls_bases, cls_dict):
        """创建新类时,将元类的方法文档拼接进新类的__doc__中"""

        new = super().__new__(metacls, cls_name, cls_bases, cls_dict)
        metacls_docs = metacls._docs()

        if new.__doc__ is None:
            new.__doc__ = metacls_docs
        else:
            new.__doc__ += metacls_docs

        return new

    def greet_user(cls):
        """打印友好问候,展示当前类名"""
        print(f"Hello, I'm the class '{cls.__name__}'!")


class UsesMeta(metaclass=Meta):
    """使用Meta作为元类的业务类"""

该方案可以实现需求,运行help(UsesMeta)就能看到元类的方法文档了,输出如下:

Help on class UsesMeta in module __main__:
class UsesMeta(builtins.object)
 |  A class that uses `Meta` as its metaclass.
 |  
 |  ----------------------------------------------
 |  
 |  从元类`Meta`继承的类方法
 |  
 |  greet_user(cls)
 |      打印友好问候,展示当前类名
 |  
 |  ----------------------------------------------
 |  
 |  从元类`Meta`继承的类属性
 |  
 |  
 |  
 |  ----------------------------------------------
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

但该方案需要编写的额外代码量太大,有没有更简洁的实现方式?

标准库的实现参考

Python标准库的Enum模块不存在这个问题,如下定义的枚举类:

from enum import Enum

class FooEnum(Enum):
    BAR = 1

运行help(FooEnum)的输出会包含元类继承的属性文档:

|  ----------------------------------------------------------------------
 |  Readonly properties inherited from enum.EnumMeta:
 |  
 |  __members__
 |      Returns a mapping of member name->value.
 |      
 |      This mapping lists all enum members, including aliases. Note that this
 |      is a read-only view of the internal mapping.

enum模块是如何实现该功能的?

使用元类而非类方法的原因

类似__iter__、__getitem__、__len__这类特殊方法无法被定义为类方法,在元类中定义这些方法可以实现更灵活的功能,enum模块就是典型的应用案例。


内容的提问来源于stack exchange,提问作者Alex Waygood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 19:57:04