es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Herencia de metaclases en Python

Tengo una metaclase simple que convierte los métodos de las clases que comienzan con “get_” en propiedades:

class PropertyConvertMetaclass(type):
    def __new__(mcs, future_class_name, future_class_parents, future_class_attr):
        new_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                if name.startswith('get_'):
                    new_attr[name[4:]] = property(val)
            else:
                new_attr[name] = val
        return type.__new__(mcs, future_class_name, future_class_parents, new_attr)

Imaginemos que tengo la clase TestClass:

class TestClass():
    def init(self, x: int):
        self._x = x

    def get_x(self):
        print("this is property")
        return self._x

Quiero que funcione de la siguiente manera: Creo una nueva clase que hereda de ambas clases:

class NewTestClass(TestClass, PropertyConvertMetaclass):
    pass

Y quiero poder reutilizar los métodos de ambas clases de la siguiente manera:

obj = NewTestClass(8)
obj.get_x()  # 8
obj.x        # 8

Según entiendo, debería crear una nueva clase llamada PropertyConvert y hacer que NewTestClass herede de ella:

class PropertyConvert(metaclass=PropertyConvertMetaclass):
    pass

class NewTestClass(TestClass, PropertyConvert):
    pass

Pero esto no ayuda, todavía no puedo usar el nuevo método de propiedad con NewTestClass. ¿Cómo puedo hacer que PropertyConvert herede todos los métodos de su hermano, sin hacer nada dentro de NewTestClass, cambiando solo PropertyConverterMetaclass o PropertyConverter? Soy nuevo en las metaclasses, así que disculpa si esta pregunta parece tonta.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    El problema con la implementación actual es que la metaclase PropertyConvertMetaclass solo convierte los métodos con el prefijo get_ en propiedades al crear NewTestClass. Cuando PropertyConvert se define como una clase separada con PropertyConvertMetaclass como su metaclase, no tiene ningún método para convertir a propiedades, por lo que la metaclase no se invoca en él.

    Para asegurarnos de que PropertyConvert herede todas las propiedades convertidas de NewTestClass, podemos modificar la metaclase para convertir los métodos en propiedades cuando se crea PropertyConvert también.

    Una forma de lograr esto es agregar una comprobación en el método __new__ de PropertyConvertMetaclass para ver si la clase que se está creando es PropertyConvert. Si lo es, simplemente podemos devolver la clase sin hacer ningún cambio. De lo contrario, podemos seguir adelante y convertir los métodos en propiedades como antes.

    Aquí está la metaclase modificada:

    class PropertyConvertMetaclass(type):
        def __new__(mcs, future_class_name, future_class_parents, future_class_attr):
            if future_class_name == 'PropertyConvert':
                return type.__new__(mcs, future_class_name, future_class_parents, future_class_attr)
    
            new_attr = {}
            for name, val in future_class_attr.items():
                if not name.startswith('__'):
                    if name.startswith('get_'):
                        new_attr[name[4:]] = property(val)
                else:
                    new_attr[name] = val
            return type.__new__(mcs, future_class_name, future_class_parents, new_attr)
    

    Ahora, cuando definamos PropertyConvert con PropertyConvertMetaclass como su metaclase, heredará todas las propiedades de NewTestClass:

    class PropertyConvert(metaclass=PropertyConvertMetaclass):
        pass
    
    class NewTestClass(TestClass, PropertyConvert):
        pass
    
    obj = NewTestClass(8)
    obj.get_x() # 8 (el método original aún funciona)
    obj.x       # 8 (la propiedad también funciona)
    

    Ten en cuenta que no necesitamos hacer ningún cambio en NewTestClass en sí. Toda la magia ocurre a nivel de la metaclase.

Comments are closed.