based python inheritance metaclass

python - based - prototype or class javascript



Comprender la metaclase y la herencia en Python (1)

Esta pregunta ya tiene una respuesta aquí:

Tengo cierta confusión con respecto a las metaclases.

Con herencia

class AttributeInitType(object): def __init__(self,**kwargs): for name, value in kwargs.items(): setattr(self, name, value) class Car(AttributeInitType): def __init__(self,**kwargs): super(Car, self).__init__(**kwargs) @property def description(self): return "%s %s %s %s" % (self.color, self.year, self.make, self.model) c = Car(make=''Toyota'', model=''Prius'', year=2005, color=''green'') print c.description

Con meta clase

class AttributeInitType(type): def __call__(self, *args, **kwargs): obj = type.__call__(self, *args) for name, value in kwargs.items(): setattr(obj, name, value) return obj class Car(object): __metaclass__ = AttributeInitType @property def description(self): return "%s %s %s %s" % (self.color, self.year, self.make, self.model) c = Car(make=''Toyota'', model=''Prius'', year=2005,color=''blue'') print c.description

Como el ejemplo anterior no es útil prácticamente, solo para entender,

Tengo algunas preguntas como,

  1. ¿Qué es el uso de metaclass y cuándo lo uso?

  2. ¿Cuál es la diferencia / similitud entre una metaclase y la herencia?

  3. ¿Dónde debería uno usar una metaclase o herencia?


1) ¿Qué es el uso de metaclass y cuándo usarlo?

Las metaclases son para las clases como las clases para los objetos. Son clases para clases (de ahí la expresión "meta").

Las metaclases son típicamente para cuando se quiere trabajar fuera de las restricciones normales de OOP.

2) ¿Cuál es la diferencia / similitud entre la metaclase y la herencia?

Una metaclase no es parte de la jerarquía de clases de un objeto, mientras que las clases base sí lo son. Por lo tanto, cuando un objeto obj.some_method() no buscará en la metaclase este método, la metaclass puede haberlo creado durante la creación de la clase o el objeto.

En este ejemplo a continuación, la metaclase MetaCar otorga a los objetos un atributo de defect basado en un número aleatorio. El atributo de defect no está definido en ninguna de las clases base de los objetos o la clase misma. Esto, sin embargo, podría haberse logrado usando solo clases.

Sin embargo (a diferencia de las clases), esta metaclase también redirige la creación de objetos; en la lista de some_cars , todos los Toyotas se crean usando la clase Car . La metaclase detecta que Car.__init__ contiene un argumento make que coincide con una clase preexistente con ese nombre y, por lo tanto, devuelve un objeto de esa clase.

Además, también some_cars que en la lista de some_cars , Car.__init__ se llama con make="GM" . Una clase de GM no se ha definido en este momento en la evaluación del programa. La metaclase detecta que no existe una clase con ese nombre en el argumento make, por lo que crea una y actualiza el espacio de nombres global (por lo que no necesita usar el mecanismo de devolución). Luego crea el objeto usando la clase recientemente definida y lo devuelve.

import random class CarBase(object): pass class MetaCar(type): car_brands = {} def __init__(cls, cls_name, cls_bases, cls_dict): super(MetaCar, cls).__init__(cls_name, cls_bases, cls_dict) if(not CarBase in cls_bases): MetaCar.car_brands[cls_name] = cls def __call__(self, *args, **kwargs): make = kwargs.get("make", "") if(MetaCar.car_brands.has_key(make) and not (self is MetaCar.car_brands[make])): obj = MetaCar.car_brands[make].__call__(*args, **kwargs) if(make == "Toyota"): if(random.randint(0, 100) < 2): obj.defect = "sticky accelerator pedal" elif(make == "GM"): if(random.randint(0, 100) < 20): obj.defect = "shithouse" elif(make == "Great Wall"): if(random.randint(0, 100) < 101): obj.defect = "cancer" else: obj = None if(not MetaCar.car_brands.has_key(self.__name__)): new_class = MetaCar(make, (GenericCar,), {}) globals()[make] = new_class obj = new_class(*args, **kwargs) else: obj = super(MetaCar, self).__call__(*args, **kwargs) return obj class Car(CarBase): __metaclass__ = MetaCar def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value) def __repr__(self): return "<%s>" % self.description @property def description(self): return "%s %s %s %s" % (self.color, self.year, self.make, self.model) class GenericCar(Car): def __init__(self, **kwargs): kwargs["make"] = self.__class__.__name__ super(GenericCar, self).__init__(**kwargs) class Toyota(GenericCar): pass colours = / [ "blue", "green", "red", "yellow", "orange", "purple", "silver", "black", "white" ] def rand_colour(): return colours[random.randint(0, len(colours) - 1)] some_cars = / [ Car(make="Toyota", model="Prius", year=2005, color=rand_colour()), Car(make="Toyota", model="Camry", year=2007, color=rand_colour()), Car(make="Toyota", model="Camry Hybrid", year=2013, color=rand_colour()), Car(make="Toyota", model="Land Cruiser", year=2009, color=rand_colour()), Car(make="Toyota", model="FJ Cruiser", year=2012, color=rand_colour()), Car(make="Toyota", model="Corolla", year=2010, color=rand_colour()), Car(make="Toyota", model="Hiace", year=2006, color=rand_colour()), Car(make="Toyota", model="Townace", year=2003, color=rand_colour()), Car(make="Toyota", model="Aurion", year=2008, color=rand_colour()), Car(make="Toyota", model="Supra", year=2004, color=rand_colour()), Car(make="Toyota", model="86", year=2013, color=rand_colour()), Car(make="GM", model="Camaro", year=2008, color=rand_colour()) ] dodgy_vehicles = filter(lambda x: hasattr(x, "defect"), some_cars) print dodgy_vehicles

3) ¿Dónde debería uno usar metaclass o herencia?

Como se menciona en esta respuesta y en los comentarios, casi siempre se usa la herencia cuando se realiza OOP. Las metaclases son para trabajar fuera de esas restricciones (consulte el ejemplo) y casi siempre no son necesarias, sin embargo, se puede lograr con ellas un flujo de programa muy avanzado y extremadamente dinámico . Esta es a la vez su fuerza y ​​su peligro .