python - based - prototype or class javascript
Comprender la metaclase y la herencia en Python (1)
Esta pregunta ya tiene una respuesta aquí:
- ¿Qué son las metaclases en Python? 15 respuestas
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,
¿Qué es el uso de metaclass y cuándo lo uso?
¿Cuál es la diferencia / similitud entre una metaclase y la herencia?
¿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 .