Comprendre la métaclasse et l'héritage en Python [dupliquer]

cette question a déjà une réponse ici:

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,

  1. Quelle est l'utilisation de metaclass et Quand dois-je l'utiliser?

  2. Quelle est la différence/similitude entre une classe de méta et l'héritage?

  3. où doit-on utiliser une classe meta ou un héritage?

42
demandé sur Peter Mortensen 2013-07-23 08:10:46

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 .

29
répondu dilbert 2018-07-04 21:03:00