Python使用attr模块创建元类触发TypeError,求Python2/3兼容方案
问题分析与解决方案
首先,咱们来拆解你遇到的 TypeError 问题:当用 @attrs 装饰元类时,attrs 会自动生成一个仅处理你定义的 my_attribute 的 __init__ 方法,但元类的 __init__ 在 Python 创建类时,必须接收四个参数:cls(被创建的类)、name(类名)、bases(父类元组)、namespace(类的命名空间)。这两个 __init__ 的参数签名完全冲突,所以触发了参数数量不匹配的错误。
接下来提供两种适配方案,同时兼顾 Python 2/3 兼容性:
方案一:手动兼容元类的 __init__ 逻辑
核心思路是禁用 attrs 自动生成的 __init__,手动编写元类的初始化方法,先处理元类必备的参数,再初始化 attrs 定义的属性。
import attr from abc import ABCMeta from six import add_metaclass # 禁用attrs自动生成__init__,手动实现兼容逻辑 @attr.define(init=False) class MetaClass(ABCMeta): my_attribute = attr.ib() def __new__(cls, name, bases, namespace): # 提取Python2中类定义里的元类参数(兼容用) kwargs = namespace.pop('__metaclass_kwargs__', {}) # 创建类实例 new_cls = super().__new__(cls, name, bases, namespace) # 手动调用__init__并传递参数 cls.__init__(new_cls, name, bases, namespace, **kwargs) return new_cls def __init__(self, name, bases, namespace, **kwargs): # 先完成元类本身的初始化逻辑 super().__init__(name, bases, namespace) # 初始化attrs定义的属性 for field in attr.fields(MetaClass): if field.name in kwargs: setattr(self, field.name, kwargs[field.name]) # 验证属性合法性(可选) attr.validate(self) # Python 3 用法 class MyClass(object, metaclass=MetaClass, my_attribute="Python3测试值"): pass # Python 2 用法 @add_metaclass(MetaClass) class MyClass2(object): __metaclass_kwargs__ = {"my_attribute": "Python2测试值"}
方案二:放弃元类装饰,转而让元类自动装饰子类
如果你只是想批量创建带属性的简单存储类,其实不需要用 attrs 装饰元类,而是让元类自动给子类加上 @attrs 装饰器,这样更简洁:
from attr import attrs, attrib from abc import ABCMeta from six import add_metaclass class MetaClass(ABCMeta): def __new__(cls, name, bases, namespace): # 自动给创建的子类添加@attrs装饰 new_cls = super().__new__(cls, name, bases, namespace) return attrs(new_cls) # Python 3 用法 class MyClass(object, metaclass=MetaClass): attr1 = attrib() attr2 = attrib() # Python 2 用法 @add_metaclass(MetaClass) class MyClass2(object): attr1 = attrib() attr2 = attrib()
这种方式既实现了批量创建存储类的需求,又避开了元类与 attrs 的初始化冲突,推荐优先考虑。
内容的提问来源于stack exchange,提问作者Torilla
相关产品推荐
相关产品推荐

