繁体   English   中英

在 Python 中使用 attr 模块和元类

[英]Using attr module and metaclasses in Python

I am trying to use the attr package to simply create a metaclass with attributes and methods to use in a further class definition in Python 3. I want to use the attrs package since I have a lot of simple storage classes that only need a few attributes关于初始化。 一切正常,除了当我试图将元类添加到主 class 时,代码失败

TypeError: __init__() takes from 1 to 2 positional arguments but 4 were given

一个简单的 MWE 将是:

from attr import attrs, attrib
from abc import ABCMeta

@attrs
class MetaClass(ABCMeta):

    my_attribute = attrib()

    def my_method(self):
        pass

class MyClass(object, metaclass=MetaClass):
    pass

对于 Python 2/3 兼容性,我通常使用六个 package 和其中的add_metaclass装饰器,但如果它可以在 ZA7F5F35426B927411FC9231B5638 中工作,我会很高兴。

attrs库确实为它正在装饰的 class 生成了一个__init__方法。 但是,元类对__new____init__方法具有明确定义的签名,只要执行class语句(连同其主体),它的 arguments 由 Python 运行时本身填充。

I mean - it is the Python runtime that fills in the arguments " class, name, bases, namespace " in a call for a metaclass __init__ , and you can't easily change that so that class, attr1, attr2 are passed instead, as attrs创建的__init__需要。

简而言之,如果不进一步查看attrs文档以查看是否可以抑制它对__init__ (以及它创建的其他一些魔术方法)的覆盖,则不能将attrs与元类一起使用。

作为记录,Python 3.7“数据类”允许“关闭”创建__init__方法,但即便如此,元类还是必须仔细考虑的东西,很难想到一个高级功能仅仅因为语言语法允许,对元类开箱即用的检测类。

总而言之,这可能是一个“x,y”问题——我建议不要尝试将第 3 方 package 与元类一起使用,只是因为您认为它的某些功能在那里可以很好地发挥作用,而是描述那是什么您想在另一个问题中使用自定义元类来实现。
元类__new__方法中的几行代码可能会为您提供所需的功能,但不会产生未知的副作用。

特别是,如果您只想将my_attribute = attrib()添加到使用自定义元类创建的所有类中,则不应尝试在元类中创建它。 也许,您只需要一个 Base 超类,根本不需要元类:

from abc import ABC
...

@attrs
class Base(ABC):
   my_attribute = attrib()

class MyClass(Base):
   pass

同样,我不知道attr库,也许它不适用于 inheritance (但我怀疑 - 它应该做得很好),然后你可以使用元类来注入属性并将@attrs装饰器应用到你的类,但不要尝试在你的元类本身上:

from attr import attrs, attrib
from abc import ABCMeta


class MetaClass(ABCMeta):

    my_attribute = attrib()

    def __new__(metacls, name, bases, namespace, **kw):
        cls = super().__new__(metacls, name, bases, namespace, **kw)
        # if the 'attrs' decorator modifies the type of "cls", 
        # the original __init__ won't be called automatically.
        # since we are inheriting from other superclass, we'd better
        # call it manually here, and suppress its automatic execution
        # bellow. 
        super(MetaClass, cls).__init__(cls, name, bases, namespace, **kw)
        cls.my_attribute = attrib()
        return attrs(cls)

    def __init__(cls, name, bases, namespace, **kw):
        pass


class MyClass(object, metaclass=MetaClass):
    pass

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM