Python 元类:从元类属性为实例创建属性

0 投票
1 回答
46 浏览
提问于 2025-04-14 17:14
class Meta(type):
    def __new__(cls, name, bases, attrs):
        # Generate a and b for the object based on x ?
        return super().__new__(cls, name, bases, attrs)

class A(metaclass=Meta):
    def __init__(self, a, b):
        self.a = a
        self.b = b

obj = A(x)

传递给 Ax 应该在元类 Meta 中被使用,并且它应该生成 A__init__ 方法所需要的属性 ab。最好是 obj 不要能访问到 x

不确定这样做是否可行或有效,但有没有办法实现呢?

1 个回答

2

你可以为这个类型提供一个新的 __call__ 方法,这个方法会从 x 计算出 ab,然后把它们传递给默认的 __call__ 方法。

class Meta(type):
    def __new__(cls, name, bases, attrs):
        # Generate a and b for the object based on x ?
        return super().__new__(cls, name, bases, attrs)

    def __call__(cls, x):
        a, b = divmod(x, 10)
        return super().__call__(a, b)

class A(metaclass=Meta):
    def __init__(self, a, b):
        self.a = a
        self.b = b

obj = A(21)  # obj = Meta.__call__(A, 21), which calls type.__call__(A, 2, 1)
assert obj.a == 2 and obj.b == 1

如果这涉及到一个 XY 问题,你可以定义一个单独的类方法,这样就不需要自定义元类了。

class A:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @classmethod
    def from_x(cls, x):
        a, b = divmod(x, 10)


obj = A.from_x(21)

这个类方法在 A 的源代码中明确说明了如何从一个参数的函数调用转换到“预期”的两个参数的初始化。这可以让调用者不必困惑于 A(21) 是怎么工作的,因为 A.__init__ 和继承的 A.__new__ 都不接受两个参数。

撰写回答