EN VI

Python Metaclass: Create attributes for instance from the attribues for metaclass?

2024-03-09 23:30:08
How to Python Metaclass: Create attributes for instance from the attribues for metaclass
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)

The x passed to A should be consumed with in the meta class Meta, and it should generate the attributes a & b that are needed by the __init__ of A. It is preferred for obj not to have access to x.

Not sure if its possible or valid, but is there a way to achieve it ?

Solution:

You can provide a new __call__ method for the type that computes a and b from x and passes them to the default __call__ method.

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

In case this is an XY problem, you can define a separate class method that avoids the need for a custom metaclass.

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)

The class method makes explicit in the source of A how you go from a one-argument function call to the "expected" two-argument initialization. It saves your caller from wondering how A(21) works when neither A.__init__ nor the inherited A.__new__ takes two arguments.

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login