Comprendre la métaclasse et l'héritage en Python [dupliquer]
cette question a déjà une réponse ici:
- que sont les métaclasses en Python? 16 réponses
j'ai une certaine confusion en ce qui concerne les méta-classes.
avec héritage
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
avec la classe meta
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
comme exemple ci-dessus n'est pas utile comme pratique, mais juste pour comprendre,
j'ai quelques questions comme,
-
Quelle est l'utilisation de metaclass et Quand dois-je l'utiliser?
-
Quelle est la différence/similitude entre une classe de méta et l'héritage?
-
où doit-on utiliser une classe meta ou un héritage?
1 réponses
1) qu'est-ce que metaclass et quand l'utiliser?
Metaclasses sont des classes que les classes sont des objets. Ce sont des classes pour classes (d'où l'expression "meta").
Metaclasses sont généralement pour quand vous voulez travailler en dehors des contraintes normales de OOP.
2) Quelle est la différence/similitude entre métaclasse et héritage?
une métaclasse ne fait pas partie du la hiérarchie des classes alors que les classes de base le sont. Ainsi, lorsqu'un objet fait obj.some_method()
, il ne cherchera pas la métaclasse pour cette méthode, mais la métaclasse peut l'avoir créée lors de la création de la classe ou de l'objet.
dans cet exemple ci-dessous, la métaclasse MetaCar
donne aux objets un attribut defect
basé sur un nombre aléatoire. L'attribut defect
n'est défini dans aucune des classes de base des objets ni dans la classe elle-même. Cela, cependant, aurait pu être réalisé utiliser uniquement les cours.
cependant (contrairement aux classes), cette métaclasse ré-achemine également la création d'objet; dans la liste some_cars
, tous les Toyota sont créés en utilisant la classe Car
. Le metaclass détecte que Car.__init__
contient un argument make
qui correspond à une classe préexistante par ce nom et retourne donc un objet de cette classe à la place.
de plus, vous noterez que dans la liste some_cars
, Car.__init__
est appelé avec make="GM"
. Une classe GM
n'a pas encore été définie dans l'évaluation du programme. Le metaclass détecte qu'une classe n'existe pas sous ce nom dans l'argument make, donc il en crée une et met à jour l'espace Nam global (donc il n'a pas besoin d'utiliser le mécanisme de retour). Il crée alors l'objet en utilisant la classe nouvellement définie et le renvoie.
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) Où doit-on utiliser métaclasse ou héritage?
comme indiqué dans cette réponse et dans les commentaires, presque toujours utiliser l'héritage en faisant OOP. Metaclasses sont pour travailler en dehors de ces contraintes (se référer à l'exemple) et est presque toujours pas nécessaire, mais certains très avancé et extrêmement dynamique flux de programme peuvent être atteints avec eux. C'est à la fois leur force et leur danger .