[解決済み] Pythonのメタクラスと継承を理解する [重複] [重複
質問
メタクラスについて、少し混乱しています。
継承で
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) メタクラスとは何か、いつ使うのか?
メタクラスは、クラスがオブジェクトにそうであるように、クラスにそうです。クラスのためのクラスです(それゆえ、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を行う場合、ほとんど常に継承を使用します。メタクラスはその制約の外で作業するためのものであり(例を参照)、ほとんどの場合必要ありませんが、いくつかの非常に高度で 非常に動的な プログラムフローを実現することができます。これはメタクラスの強みであり 危険 .
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Jupyterノートブックでenv変数を設定する方法
-
[解決済み] Pythonでコード行間にかかる時間を測定するには?
-
[解決済み] Django のテストデータベースをメモリ上だけで動作させるには?
-
[解決済み] Spyderを仮想環境で動作させるには?
-
[解決済み] PyMongoで.sortを使用する
-
[解決済み] tensorflowのCPUのみのインストールでダイナミックライブラリ 'cudart64_101.dll' を読み込めなかった
-
[解決済み] Celeryタスクのユニットテストはどのように行うのですか?
-
[解決済み] virtualenvsはどこに作成するのですか?
-
[解決済み] Pythonでランダムなファイル名を生成する最良の方法
-
[解決済み] djangoのQueryDictをPythonのDictに変更するには?