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

Python中如何获取类/对象的纯魔法方法?

如何区分纯魔法方法与普通双下划线方法(不触发__getattr__的方法)

我需要判断一个方法是否是Python解释器直接调用的纯魔法方法——这类方法不会经过__getattr__处理,而非仅仅是带前后双下划线的普通dunder方法。目前的实现依赖一个庞大的__ignore_attributes__集合来排除不需要代理的项,但这个集合维护起来很麻烦,希望能简化方案,让它更健壮。

现有实现代码如下:

class ParameterizedMeta(ABCMeta):

    @_tp_cache
    def __getitem__(cls, *values):
        # methods have default implementation not places here
        values = cls.__subclass_parseargs__(*values)
        parameters = cls.__subclass_parameters__(*values)
        name = cls.__subclass_name__(parameters)
        bases = cls.__subclass_bases__(parameters)
        subclass = type(name, bases, parameters)
        subclass.__subclass_post_init__()
        return subclass


__ignore_attributes__ = {
    "__class__",
    "__mro__",
    "__new__",
    "__init__",

    "__setattr__",
    "__getattr__",
    "__delattr__",
    "__getattribute__",
    "__eq__",
    "__ne__",

    "__init_subclass__",
    "__class_getitem__",
    "__subclasshook__",

    "__module__",
    "__name__",
    "__qualname__",

    "__dict__",

    "__slots__",
    "__size__",
    "__sizeof__",
    "__weakref__",
    "__abstractmethods__",

    "__orig_class__",
    "__post_init__",

    "__type__",
    ...
}

__force_attributes__ = [
    "__hash__",
    "__str__",
    "__repr__",
]

__proxy_meta_bypass__ = {"__type__", ...}


class ProxyMeta(ParameterizedMeta):
    __type__: type[Serializable]

    def __getattr__(self, item) -> Any:
        # If requested attribute is one of predefined prevent infinite recursion
        if item in __proxy_meta_bypass__ :
            raise AttributeError
        return getattr(self.__type__, item)

    def __subclass_post_init__(self):
        if not isinstance(self.__type__, type):
            return

        attributes = set(dir(self.__type__) + __force_attributes__)

        def delegate(attribute: str):
            def proxy(s, *args, **kwargs):
                func = getattr(s.__value__, attribute)
                return func(*args, **kwargs)

            proxy.__name__ = str(attributes)

            return proxy

        def check_is_magic(attribute: str):
            if not attribute.startswith("__") or not attribute.endswith("__"):
                return False
            if attribute in __ignore_attributes__:
                return False
            value = getattr(self.__type__, attribute)
            if not callable(value):
                return False
            return True

        for name in filter(check_is_magic, attributes):
            setattr(self, name, delegate(name))

核心改进思路

Python中,只有解释器直接调用的魔法方法会绕过__getattr__/__getattribute__的属性查找流程,其他双下划线方法(包括用户自定义的)都会正常走属性查找。我们可以利用这一特性,抛弃维护庞大忽略集合的思路,转而通过白名单筛选或自动识别默认魔法方法来实现更健壮的方案。

方案1:维护核心魔法方法白名单

只保留那些解释器会直接调用、必须代理的核心魔法方法,避免冗余的忽略项维护:

class ParameterizedMeta(ABCMeta):
    @_tp_cache
    def __getitem__(cls, *values):
        values = cls.__subclass_parseargs__(*values)
        parameters = cls.__subclass_parameters__(*values)
        name = cls.__subclass_name__(parameters)
        bases = cls.__subclass_bases__(parameters)
        subclass = type(name, bases, parameters)
        subclass.__subclass_post_init__()
        return subclass

# 仅保留需要代理的核心魔法方法(解释器直接调用,不走__getattr__)
REQUIRED_MAGIC_METHODS = {
    "__str__", "__repr__", "__hash__", "__eq__", "__ne__",
    "__call__", "__len__", "__getitem__", "__setitem__", "__delitem__",
    "__iter__", "__next__", "__contains__", "__enter__", "__exit__",
    "__add__", "__sub__", "__mul__", "__truediv__", "__floordiv__",
    "__mod__", "__pow__", "__lt__", "__le__", "__gt__", "__ge__",
    "__bool__", "__int__", "__float__", "__complex__", "__bytes__",
}

__force_attributes__ = list(REQUIRED_MAGIC_METHODS)
__proxy_meta_bypass__ = {"__type__"}

class ProxyMeta(ParameterizedMeta):
    __type__: type[Serializable]

    def __getattr__(self, item) -> Any:
        if item in __proxy_meta_bypass__:
            raise AttributeError
        return getattr(self.__type__, item)

    def __subclass_post_init__(self):
        if not isinstance(self.__type__, type):
            return

        def delegate(attribute: str):
            def proxy(s, *args, **kwargs):
                func = getattr(s.__value__, attribute)
                return func(*args, **kwargs)
            proxy.__name__ = attribute
            return proxy

        def check_is_required_magic(attribute: str):
            return attribute in REQUIRED_MAGIC_METHODS and callable(getattr(self.__type__, attribute, None))

        for name in filter(check_is_required_magic, REQUIRED_MAGIC_METHODS):
            setattr(self, name, delegate(name))

方案2:自动筛选默认类的魔法方法

通过对比dir(type)的默认属性,自动识别需要代理的魔法方法,无需手动维护白名单:

class ParameterizedMeta(ABCMeta):
    @_tp_cache
    def __getitem__(cls, *values):
        values = cls.__subclass_parseargs__(*values)
        parameters = cls.__subclass_parameters__(*values)
        name = cls.__subclass_name__(parameters)
        bases = cls.__subclass_bases__(parameters)
        subclass = type(name, bases, parameters)
        subclass.__subclass_post_init__()
        return subclass

__proxy_meta_bypass__ = {"__type__"}

class ProxyMeta(ParameterizedMeta):
    __type__: type[Serializable]

    def __getattr__(self, item) -> Any:
        if item in __proxy_meta_bypass__:
            raise AttributeError
        return getattr(self.__type__, item)

    def __subclass_post_init__(self):
        if not isinstance(self.__type__, type):
            return

        def delegate(attribute: str):
            def proxy(s, *args, **kwargs):
                func = getattr(s.__value__, attribute)
                return func(*args, **kwargs)
            proxy.__name__ = attribute
            return proxy

        # 自动获取type对象的默认魔法方法
        default_type_magic = {attr for attr in dir(type) if attr.startswith("__") and attr.endswith("__")}
        # 排除非方法属性(如__name__、__module__)
        default_magic_methods = {attr for attr in default_type_magic if callable(getattr(type, attr, None))}
        # 补充需要强制代理的方法
        target_methods = default_magic_methods | {"__hash__", "__str__", "__repr__"}

        def check_is_magic_method(attribute: str):
            if attribute not in target_methods:
                return False
            value = getattr(self.__type__, attribute, None)
            return callable(value)

        for name in filter(check_is_magic_method, target_methods):
            setattr(self, name, delegate(name))

改进后的优势

  • 无需维护庞大的忽略集合,聚焦需要代理的核心方法,减少维护成本。
  • 利用Python解释器的特性,确保只代理真正绕过__getattr__的方法,逻辑更精准。
  • 自动筛选方案可适配Python版本变化,无需手动更新方法列表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 15:05:14