1. ホーム
  2. python

[解決済み] Pythonのメタクラスと継承を理解する [重複] [重複

2023-07-13 08:26:35

質問

メタクラスについて、少し混乱しています。

継承で

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

メタクラス付き

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

上記の例は実用的なものではなく、あくまで理解のためのものです。

というような質問があります。

  1. メタクラスと継承の違い/類似点は何ですか?

  2. メタクラスや継承はどこで使うべきでしょうか?

どのように解決するのですか?

1) メタクラスとは何か、いつ使うのか?

メタクラスは、クラスがオブジェクトにそうであるように、クラスにそうです。クラスのためのクラスです(それゆえ、quot;meta"という表現があります)。

メタクラスは通常、OOPの通常の制約の外側で作業したいときのためにあります。

2) メタクラスと継承の違い・類似点は?

メタクラスはオブジェクトのクラス階層に含まれませんが、ベースクラスは含まれます。そのため、オブジェクトが obj.some_method() を実行するとき、このメソッドのためにメタクラスを検索することはありませんが、メタクラスはクラスまたはオブジェクトの生成時にこのメソッドを生成しているかもしれません。

この下の例では、メタクラスが MetaCar はオブジェクトに defect 属性を与えます。そのため defect 属性は、オブジェクトの基底クラスやクラス自体のいずれでも定義されていません。しかし、これはクラスのみを使用して実現することができました。

しかし(クラスとは異なり)、このメタクラスはオブジェクトの生成も再ルーティングします。 some_cars リストでは、すべての Toyotas は Car クラスで作成されます。メタクラスが検出するのは Car.__init__ には make 引数があり、その引数はその名前の既存のクラスにマッチするので、 代わりにそのクラスのオブジェクトを返します。

さらに、このクラスは some_cars のリストに Car.__init__ が呼び出されるのは make="GM" . A GM クラスはプログラムの評価のこの時点では定義されていません。メタクラスは make 引数でその名前のクラスが存在しないことを検出し、クラスを作成し、グローバルな名前空間を更新します(したがって、return メカニズムを使用する必要はありません)。そして、新しく定義されたクラスを使用してオブジェクトを作成し、それを返します。

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) メタクラスや継承はどこで使うべきでしょうか?

この回答やコメントで述べたように、OOPを行う場合、ほとんど常に継承を使用します。メタクラスはその制約の外で作業するためのものであり(例を参照)、ほとんどの場合必要ありませんが、いくつかの非常に高度で 非常に動的な プログラムフローを実現することができます。これはメタクラスの強みであり 危険 .