Python 元类:从元类属性为实例创建属性
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)
传递给 A
的 x
应该在元类 Meta
中被使用,并且它应该生成 A
的 __init__
方法所需要的属性 a
和 b
。最好是 obj
不要能访问到 x
。
不确定这样做是否可行或有效,但有没有办法实现呢?
1 个回答
2
你可以为这个类型提供一个新的 __call__
方法,这个方法会从 x
计算出 a
和 b
,然后把它们传递给默认的 __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__
都不接受两个参数。