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
相关产品推荐
相关产品推荐

