理解Python中的元类和继承

Understanding metaclass and inheritance in Python

本问题已经有最佳答案,请猛点这里访问。

关于元类,我有些困惑。

有继承权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

元类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)元类的用途是什么,以及何时使用它?

    元类是对类的,类是对对象的。它们是类的类(因此表达式"meta")。

    元类通常用于在OOP的正常约束之外工作的情况。

    2)元类和继承的区别/相似之处是什么?

    元类不是对象类层次结构的一部分,而基类是。因此,当一个对象执行obj.some_method()操作时,它不会为这个方法搜索元类,但是元类可能在类或对象的创建过程中创建了它。

    在下面的示例中,元类MetaCar基于随机数给对象一个defect属性。defect属性未在任何对象的基类或类本身中定义。然而,这只能通过使用类来实现。

    但是(与类不同),这个元类也重新路由对象创建;在some_cars列表中,所有的toyota都是使用Car类创建的。元类检测到Car.__init__包含一个make参数,该参数用该名称匹配一个预先存在的类,因此返回该类的对象。

    此外,您还将注意到在some_cars列表中,Car.__init__make="GM"一起调用。GM类目前尚未在项目评估中定义。元类检测到make参数中不存在同名的类,因此它创建一个类并更新全局命名空间(因此不需要使用返回机制)。然后,它使用新定义的类创建对象并返回它。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    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时几乎总是使用继承。元类用于在这些约束之外工作(请参阅示例),并且几乎总是不必要的,但是可以使用它们实现一些非常高级和非常动态的程序流。这既是他们的力量,也是他们的危险。