了解Python中的元类和继承 [英] Understanding metaclass and inheritance in 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)元类的用途是什么?何时使用?
元类是类,而类是对象.它们是类的类(因此表达为元").
元类通常用于您要在OOP的常规约束之外工作的情况.
2)元类和继承之间的区别/相似之处是什么?
元类不属于对象的类层次结构,而基类则属于对象.因此,当对象执行obj.some_method()
时,它将不会在元类中搜索此方法,但是该元类可能是在类或对象的创建期间创建的.
在下面的示例中,元类MetaCar
根据随机数为对象提供defect
属性. defect
属性未在任何对象的基类或类本身中定义.但是,这只能使用类来实现.
但是(与类不同),该元类还重新路由对象的创建;在some_cars
列表中,所有Toyota均使用Car
类创建.元类检测到Car.__init__
包含一个make
自变量,该自变量通过该名称与现有类匹配,因此返回该类的对象.
此外,您还将注意到在some_cars
列表中,Car.__init__
用make="GM"
调用.在程序的评估中,此时尚未定义GM
类.元类在make参数中检测到该名称不存在一个类,因此它创建一个类并更新全局名称空间(因此不需要使用返回机制).然后,它使用新定义的类创建对象并返回它.
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时几乎总是使用继承.元类用于在这些约束之外工作(请参见示例),并且几乎总是没有必要,但是使用它们可以实现一些非常高级且极其动态的程序流.这既是他们的力量,也是他们的危险.
I have some confusion regarding meta-classes.
With inheritance
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
With meta class
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
As above example is not useful as practically but just for understanding,
I have some questions Like,
What is the difference/similarity between a meta class and inheritance?
Where should one use a meta class or inheritance?
1) What is use of metaclass and when to use it?
Metaclasses are to classes as classes are to objects. They are classes for classes (hence the expression "meta").
Metaclasses are typically for when you want to work outside of the normal constraints of OOP.
2) What is difference/similarity between metaclass and inheritance?
A metaclass is not part of an object's class hierarchy whereas base classes are. So when an object does obj.some_method()
it will not search the metaclass for this method however the metaclass may have created it during the class' or object's creation.
In this example below, the metaclass MetaCar
gives objects a defect
attribute based on a random number. The defect
attribute is not defined in any of the objects' base classes or the class itself. This, however, could have been achieved using classes only.
However (unlike classes), this metaclass also re-routes object creation; in the some_cars
list, all the Toyotas are created using the Car
class. The metaclass detects that Car.__init__
contains a make
argument that matches a pre-existing class by that name and so returns a object of that class instead.
Additionally, you'll also note that in the some_cars
list, Car.__init__
is called with make="GM"
. A GM
class has not been defined at this point in the program's evaluation. The metaclass detects that a class doesn't exist by that name in the make argument, so it creates one and updates the global namespace (so it doesn't need to use the return mechanism). It then creates the object using the newly defined class and returns it.
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) Where should one use metaclass or inheritance?
As mentioned in this answer and in the comments, almost always use inheritance when doing OOP. Metaclasses are for working outside those constraints (refer to example) and is almost always not necessary however some very advanced and extremely dynamic program flow can be achieved with them. This is both their strength and their danger.
这篇关于了解Python中的元类和继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!