Python之Metaclass

1,577次阅读
没有评论

类即对象

在理解元类之前,需要先掌握Python中的类,Python中类的概念与SmallTalk中类的概念相似。

Python之Metaclass 在大多数语言中,类是用来描述如何创建对象的代码段,这在Python中也是成立的:

<span class="hljs-prompt">>>> </span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ObjectCreator</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-prompt">... </span>      <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">... </span>

<span class="hljs-prompt">>>> </span>my_object = ObjectCreator()
<span class="hljs-prompt">>>> </span>print(my_object)
<__main__.ObjectCreator object at <span class="hljs-number">0x8974f2c</span>>

Python中,类其实也是对象。当我们使用关键字class的时候,Python会执行这段代码,然后生成一个对象。下面的代码在内存中创建一个对象ObjectCreator:

<span class="hljs-prompt">>>> </span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ObjectCreator</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-prompt">... </span>      <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">... </span>

当一个对象具有创建对象的能力时,就称该对象为类。

所以类本质上还是一个对象,因此它具有以下属性:

  • 可以将它赋值给其它变量
  • 可以对它进行复制
  • 可以给它添加属性
  • 可以将它传递给函数作为参数

例如:

<span class="hljs-prompt">>>> </span>print(ObjectCreator) <span class="hljs-comment"># you can print a class because it's an object</span>
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">ObjectCreator</span>'>
>>> <span class="hljs-title">def</span> <span class="hljs-title">echo</span><span class="hljs-params">(o)</span>:</span>
<span class="hljs-prompt">... </span>      print(o)
<span class="hljs-prompt">... </span>
<span class="hljs-prompt">>>> </span>echo(ObjectCreator) <span class="hljs-comment"># you can pass a class as a parameter</span>
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">ObjectCreator</span>'>
>>> <span class="hljs-title">print</span><span class="hljs-params">(hasattr<span class="hljs-params">(ObjectCreator, <span class="hljs-string">'new_attribute'</span>)</span>)</span>
<span class="hljs-title">False</span>
>>> <span class="hljs-title">ObjectCreator</span>.<span class="hljs-title">new_attribute</span> = '<span class="hljs-title">foo</span>' # <span class="hljs-title">you</span> <span class="hljs-title">can</span> <span class="hljs-title">add</span> <span class="hljs-title">attributes</span> <span class="hljs-title">to</span> <span class="hljs-title">a</span> <span class="hljs-title">class</span>
>>> <span class="hljs-title">print</span><span class="hljs-params">(hasattr<span class="hljs-params">(ObjectCreator, <span class="hljs-string">'new_attribute'</span>)</span>)</span>
<span class="hljs-title">True</span>
>>> <span class="hljs-title">print</span><span class="hljs-params">(ObjectCreator.new_attribute)</span>
<span class="hljs-title">foo</span>
>>> <span class="hljs-title">ObjectCreatorMirror</span> = <span class="hljs-title">ObjectCreator</span> # <span class="hljs-title">you</span> <span class="hljs-title">can</span> <span class="hljs-title">assign</span> <span class="hljs-title">a</span> <span class="hljs-title">class</span> <span class="hljs-title">to</span> <span class="hljs-title">a</span> <span class="hljs-title">variable</span>
>>> <span class="hljs-title">print</span><span class="hljs-params">(ObjectCreatorMirror.new_attribute)</span>
<span class="hljs-title">foo</span>
>>> <span class="hljs-title">print</span><span class="hljs-params">(ObjectCreatorMirror<span class="hljs-params">()</span>)</span>
<<span class="hljs-title">__main__</span>.<span class="hljs-title">ObjectCreator</span> <span class="hljs-title">object</span> <span class="hljs-title">at</span> 0<span class="hljs-title">x8997b4c</span>></span>

动态创建类

既然类就是对象,那我们就可以像创建其他对象一样动态创建类。 首先,在函数中使用class创建一个类:

<span class="hljs-prompt">>>> </span><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">choose_class</span><span class="hljs-params">(name)</span>:</span>
<span class="hljs-prompt">... </span>    <span class="hljs-keyword">if</span> name == <span class="hljs-string">'foo'</span>:
<span class="hljs-prompt">... </span>        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-prompt">... </span>            <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">... </span>        <span class="hljs-keyword">return</span> Foo <span class="hljs-comment"># return the class, not an instance</span>
<span class="hljs-prompt">... </span>    <span class="hljs-keyword">else</span>:
<span class="hljs-prompt">... </span>        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bar</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-prompt">... </span>            <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">... </span>        <span class="hljs-keyword">return</span> Bar
<span class="hljs-prompt">... </span>    
<span class="hljs-prompt">>>> </span>MyClass = choose_class(<span class="hljs-string">'foo'</span>) 
<span class="hljs-prompt">>>> </span>print(MyClass) <span class="hljs-comment"># the function returns a class, not an instance</span>
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">Foo</span>'>
>>> <span class="hljs-title">print</span><span class="hljs-params">(MyClass<span class="hljs-params">()</span>)</span> # <span class="hljs-title">you</span> <span class="hljs-title">can</span> <span class="hljs-title">create</span> <span class="hljs-title">an</span> <span class="hljs-title">object</span> <span class="hljs-title">from</span> <span class="hljs-title">this</span> <span class="hljs-title">class</span>
<<span class="hljs-title">__main__</span>.<span class="hljs-title">Foo</span> <span class="hljs-title">object</span> <span class="hljs-title">at</span> 0<span class="hljs-title">x89c6d4c</span>></span>

但是上面的例子也称不上是完全动态的创建类,因为我们还需要在其中编写整个类的代码。 既然类就是对象,那么它们肯定是通过某个东西来创建的。当使用class关键字的时候,Python会自动创建类,Python也提供了方法让我们手动来创建类。

还记得type()函数吗?这个函数可以获取对象的类型。

<span class="hljs-prompt">>>> </span>print(type(<span class="hljs-number">1</span>))
<type <span class="hljs-string">'int'</span>>
<span class="hljs-prompt">>>> </span>print(type(<span class="hljs-string">"1"</span>))
<type <span class="hljs-string">'str'</span>>
<span class="hljs-prompt">>>> </span>print(type(ObjectCreator))
<type <span class="hljs-string">'type'</span>>
<span class="hljs-prompt">>>> </span>print(type(ObjectCreator()))
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">ObjectCreator</span>'></span>

type还有另外一个功能,那就是创建类。type使用类的相关描述作为参数,然后返回一个类。 type创建类的语法如下:

<span class="hljs-built_in">type</span>(类名,基类元组(可以为空,用于继承), 包含属性或函数的字典)

例如:

<span class="hljs-prompt">>>> </span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyShinyClass</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-prompt">... </span>      <span class="hljs-keyword">pass</span>

上面的类可以使用下面的方法手动创建:

<span class="hljs-prompt">>>> </span>MyShinyClass = type(<span class="hljs-string">'MyShinyClass'</span>, (), {}) <span class="hljs-comment"># returns a class object</span>
<span class="hljs-prompt">>>> </span>print(MyShinyClass)
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">MyShinyClass</span>'>
>>> <span class="hljs-title">print</span><span class="hljs-params">(MyShinyClass<span class="hljs-params">()</span>)</span> # <span class="hljs-title">create</span> <span class="hljs-title">an</span> <span class="hljs-title">instance</span> <span class="hljs-title">with</span> <span class="hljs-title">the</span> <span class="hljs-title">class</span>
<<span class="hljs-title">__main__</span>.<span class="hljs-title">MyShinyClass</span> <span class="hljs-title">object</span> <span class="hljs-title">at</span> 0<span class="hljs-title">x8997cec</span>></span>

type也接收一个字典参数来定义类中的属性:

<span class="hljs-prompt">>>> </span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-prompt">... </span>      bar = <span class="hljs-keyword">True</span>

等价于

<span class="hljs-prompt">>>> </span>Foo = type(<span class="hljs-string">'Foo'</span>, (), {<span class="hljs-string">'bar'</span>:<span class="hljs-keyword">True</span>})

通过type创建的类使用方式跟普通类一样:

<span class="hljs-prompt">>>> </span>print(Foo)
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">Foo</span>'>
>>> <span class="hljs-title">print</span><span class="hljs-params">(Foo.bar)</span>
<span class="hljs-title">True</span>
>>> <span class="hljs-title">f</span> = <span class="hljs-title">Foo</span><span class="hljs-params">()</span>
>>> <span class="hljs-title">print</span><span class="hljs-params">(f)</span>
<<span class="hljs-title">__main__</span>.<span class="hljs-title">Foo</span> <span class="hljs-title">object</span> <span class="hljs-title">at</span> 0<span class="hljs-title">x8a9b84c</span>>
>>> <span class="hljs-title">print</span><span class="hljs-params">(f.bar)</span>
<span class="hljs-title">True</span></span>

当然也可以继承:

<span class="hljs-prompt">>>> </span>  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooChild</span><span class="hljs-params">(Foo)</span>:</span>
<span class="hljs-prompt">... </span>        <span class="hljs-keyword">pass</span>

等价于:

<span class="hljs-prompt">>>> </span>FooChild = type(<span class="hljs-string">'FooChild'</span>, (Foo,), {})
<span class="hljs-prompt">>>> </span>print(FooChild)
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">FooChild</span>'>
>>> <span class="hljs-title">print</span><span class="hljs-params">(FooChild.bar)</span> # <span class="hljs-title">bar</span> <span class="hljs-title">is</span> <span class="hljs-title">inherited</span> <span class="hljs-title">from</span> <span class="hljs-title">Foo</span>
<span class="hljs-title">True</span></span>

最后,我们可能还想给类添加方法,可以先定义一个函数,然后将它以属性的方式赋予给类。

<span class="hljs-prompt">>>> </span><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo_bar</span><span class="hljs-params">(self)</span>:</span>
<span class="hljs-prompt">... </span>      print(self.bar)
<span class="hljs-prompt">... </span>
<span class="hljs-prompt">>>> </span>FooChild = type(<span class="hljs-string">'FooChild'</span>, (Foo,), {<span class="hljs-string">'echo_bar'</span>: echo_bar})
<span class="hljs-prompt">>>> </span>hasattr(Foo, <span class="hljs-string">'echo_bar'</span>)
<span class="hljs-keyword">False</span>
<span class="hljs-prompt">>>> </span>hasattr(FooChild, <span class="hljs-string">'echo_bar'</span>)
<span class="hljs-keyword">True</span>
<span class="hljs-prompt">>>> </span>my_foo = FooChild()
<span class="hljs-prompt">>>> </span>my_foo.echo_bar()
<span class="hljs-keyword">True</span>

而且,我们还可以在动态创建类之后,给类添加更多的方法和属性:

<span class="hljs-prompt">>>> </span><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo_bar_more</span><span class="hljs-params">(self)</span>:</span>
<span class="hljs-prompt">... </span>      print(<span class="hljs-string">'yet another method'</span>)
<span class="hljs-prompt">... </span>
<span class="hljs-prompt">>>> </span>FooChild.echo_bar_more = echo_bar_more
<span class="hljs-prompt">>>> </span>hasattr(FooChild, <span class="hljs-string">'echo_bar_more'</span>)
<span class="hljs-keyword">True</span>

什么是元类?

通常,我们定义类来创建对象,但是现在我们知道类也是对象。那么是通过什么来创建类呢?答案就是元类。你可以想象关系如下:

MyClass = MetaClass()
MyObject = MyClass()

你已经知道使用type可以创建类:

MyClass = type(<span class="hljs-string">'MyClass'</span>, (), {})

那是因为type函数实际上就是一个元类,Python使用type作为元类来创建所有的类。 通过检查class属性,我们可以知道,其实Python中任何数据类型都是对象,包括整型、字符串、函数以及类,它们都是对象。它们都是从类中创建的。

<span class="hljs-prompt">>>> </span>age = <span class="hljs-number">35</span>
<span class="hljs-prompt">>>> </span>age.__class__
<type <span class="hljs-string">'int'</span>>
<span class="hljs-prompt">>>> </span>name = <span class="hljs-string">'bob'</span>
<span class="hljs-prompt">>>> </span>name.__class__
<type <span class="hljs-string">'str'</span>>
<span class="hljs-prompt">>>> </span><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span>:</span> <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">>>> </span>foo.__class__
<type <span class="hljs-string">'function'</span>>
<span class="hljs-prompt">>>> </span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bar</span><span class="hljs-params">(object)</span>:</span> <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">>>> </span>b = Bar()
<span class="hljs-prompt">>>> </span>b.__class__
<<span class="hljs-class"><span class="hljs-keyword">class</span> '<span class="hljs-title">__main__</span>.<span class="hljs-title">Bar</span>'></span>

那么__class____class__是什么呢?

<span class="hljs-prompt">>>> </span>age.__class__.__class__
<type <span class="hljs-string">'type'</span>>
<span class="hljs-prompt">>>> </span>name.__class__.__class__
<type <span class="hljs-string">'type'</span>>
<span class="hljs-prompt">>>> </span>foo.__class__.__class__
<type <span class="hljs-string">'type'</span>>
<span class="hljs-prompt">>>> </span>b.__class__.__class__
<type <span class="hljs-string">'type'</span>>

所以类其实就是通过元类来创建的,你可以将元类称之为类工厂。 type是内置的元类,Python默认使用它来创建类。当然,我们也可以定义属于我们自己的元类。

metaclass属性

当我们创建类的时候,可以给它添加metaclass属性:

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span><span class="hljs-params">(object)</span>:</span>
  __metaclass__ = something...
  [...]

如果我们定义了metaclass属性,Python就会使用这个元类来创建类Foo。 注意,编译器首先读取class Foo(object),这时并不会在内存中创建Foo类。Python会继续查找类定义中的__meatclass__,如果找到了,就使用它来创建类Foo,如果没有找到,就使用type来创建类。 所以对于以下代码:

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span><span class="hljs-params">(Bar)</span>:</span>
  <span class="hljs-keyword">pass</span>

Python工作流程如下:

  • 首先检查Foo中是否具有属性__metaclass__
  • 如果找到,就使用__metaclass__定义的元类在内存中创建一个类对象。
  • 如果在类定义中没有找到这个属性,就在模块级别中进行查找。
  • 如果还是没有找到,就会使用父类Bar中的元类来创建类。

注意:类中的__metaclass__属性不会被子类继承,但是父类中的__class__会被继承。

自定义元类

元类的主要作用是在创建类的时候自动改变类。 例如,想要实现模块中所有的类属性都是大写格式。可以定义模块级别的__metaclass__来实现。 这样模块中所有的类都是通过这个元类来创建的。

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upper_attr</span><span class="hljs-params">(future_class_name, future_class_parents, future_class_attr)</span>:</span>
  <span class="hljs-string">"""
    返回一个类,该类的所有属性名的都为大写
  """</span>
  <span class="hljs-comment"># 将不是__开头的属性名转为大写字母</span>
  uppercase_attr = {}
  <span class="hljs-keyword">for</span> name, val <span class="hljs-keyword">in</span> future_class_attr.items():
      <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> name.startswith(<span class="hljs-string">'__'</span>):
          uppercase_attr[name.upper()] = val
      <span class="hljs-keyword">else</span>:
          uppercase_attr[name] = val
  <span class="hljs-comment"># 使用type创建类</span>
  <span class="hljs-keyword">return</span> type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr <span class="hljs-comment"># 定义模块级别的元类,这样模块中所有类都会使用该元类创建</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span><span class="hljs-params">()</span>:</span> 
  <span class="hljs-comment"># 注意,新式类不支持模块级别的元类,但是可以在类中定义__metaclass__</span>
  bar = <span class="hljs-string">'bip'</span>

print(hasattr(Foo, <span class="hljs-string">'bar'</span>))
<span class="hljs-comment"># 输出: False</span>
print(hasattr(Foo, <span class="hljs-string">'BAR'</span>))
<span class="hljs-comment"># 输出: True</span>

f = Foo()
print(f.BAR)
<span class="hljs-comment"># Out: 'bip'</span>

也可以将metaclass定义为一个真正的类:

<span class="hljs-comment"># 记住type还是一个类,所以可以继承它</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UpperAttrMetaclass</span><span class="hljs-params">(type)</span>:</span> 
    <span class="hljs-comment"># __new__ 会在__init__之前调用,它会创建并返回一个实例</span>
    <span class="hljs-comment"># 而__init__仅用于初始化,进行一些参数的配置 </span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span><span class="hljs-params">(upperattr_metaclass, future_class_name, 
                future_class_parents, future_class_attr)</span>:</span>
        uppercase_attr = {}
        <span class="hljs-keyword">for</span> name, val <span class="hljs-keyword">in</span> future_class_attr.items():
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> name.startswith(<span class="hljs-string">'__'</span>):
                uppercase_attr[name.upper()] = val
            <span class="hljs-keyword">else</span>:
                uppercase_attr[name] = val

        <span class="hljs-keyword">return</span> type(future_class_name, future_class_parents, uppercase_attr)

但是上面的做法并不符合OOP的思想,因为它直接调用了type方法,实际上可以调用type__new__方法。

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UpperAttrMetaclass</span><span class="hljs-params">(type)</span>:</span> 
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span><span class="hljs-params">(upperattr_metaclass, future_class_name, 
                future_class_parents, future_class_attr)</span>:</span>
        uppercase_attr = {}
        <span class="hljs-keyword">for</span> name, val <span class="hljs-keyword">in</span> future_class_attr.items():
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> name.startswith(<span class="hljs-string">'__'</span>):
                uppercase_attr[name.upper()] = val
            <span class="hljs-keyword">else</span>:
                uppercase_attr[name] = val
        <span class="hljs-comment"># 调用type.__new__方法 </span>
        <span class="hljs-keyword">return</span> type.__new__(upperattr_metaclass, future_class_name, 
                            future_class_parents, uppercase_attr)

你可能注意到参数upperattr_metaclass, 它代表要实例化的类。当然,我这里取这么个复杂的名字主要是为了明确它的含义。但是,就像self参数一样,所有参数都有其习惯性命名。所以生产环境下的metaclass定义如下:

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UpperAttrMetaclass</span><span class="hljs-params">(type)</span>:</span> 
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span><span class="hljs-params">(cls, clsname, bases, dct)</span>:</span>
        uppercase_attr = {}
        <span class="hljs-keyword">for</span> name, val <span class="hljs-keyword">in</span> dct.items():
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> name.startswith(<span class="hljs-string">'__'</span>):
                uppercase_attr[name.upper()] = val
            <span class="hljs-keyword">else</span>:
                uppercase_attr[name] = val
        <span class="hljs-keyword">return</span> type.__new__(cls, clsname, bases, uppercase_attr)

更好的方式是使用super方法,以便减轻这种继承关系。

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UpperAttrMetaclass</span><span class="hljs-params">(type)</span>:</span> 
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span><span class="hljs-params">(cls, clsname, bases, dct)</span>:</span>
        uppercase_attr = {}
        <span class="hljs-keyword">for</span> name, val <span class="hljs-keyword">in</span> dct.items():
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> name.startswith(<span class="hljs-string">'__'</span>):
                uppercase_attr[name.upper()] = val
            <span class="hljs-keyword">else</span>:
                uppercase_attr[name] = val

        <span class="hljs-keyword">return</span> super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

元类实际上做了以下三方面的工作:

  • 干涉创建类的过程
  • 修改类
  • 返回修改之后的类

为什么使用类而不是函数来定义元类?

理由如下:

  • 目的更明确,当你阅读UpperAttrMetaclass(type)的时候,你知道它用来做什么。
  • 可以使用面向对象编程,元类可以继承自其它元类,还可以覆盖父类方法。
  • 可以更好的组织代码结构。元类通常用于处理比较复杂的情况。
  • 可以为__new____init____call__编写钩子,为后续开发者提供便利。

为什么使用元类?

现在,终极问题来了,为什么要使用元类这种模糊且容易出错的功能? 一般情况下,我们并不会使用元类,99%的开发者并不会用到元类,所以一般不用考虑这个问题。 元类主用用于创建API,一个典型的例子就是Django的ORM。 它让我们可以这样定义一个类:

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span><span class="hljs-params">(models.Model)</span>:</span>
  name = models.CharField(max_length=<span class="hljs-number">30</span>)
  age = models.IntegerField()

运行下面的代码:

guy = Person(name=<span class="hljs-string">'bob'</span>, age=<span class="hljs-string">'35'</span>)
print(guy.age)

返回的结果是int类型而不是IntegerField对象。这是因为models.Model使用了元类,它会将Python中定义的字段转换成数据库中的字段。 通过使用元类,Django将复杂的接口转换成简单的接口。

总结

首先,我们知道了类其实就是可以创建实例的对象。而类又是通过元类来创建的。

<span class="hljs-prompt">>>> </span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span><span class="hljs-params">(object)</span>:</span> <span class="hljs-keyword">pass</span>
<span class="hljs-prompt">>>> </span>id(Foo)
<span class="hljs-number">142630324</span>

Python中所有数据类型都是对象,它们要么是类的实例要么是元类的实例。 除了type,它实际上是自身的元类。这一点没法在Python中重现,因为它是在编译阶段实现的。

其次, 元类都是复杂的,对于一般的类是用不着的。可以使用以下两种技巧修改类:

  • monkey patch
  • 类修饰器

当你需要修改类的时候,99%的情况下可以使用元类。但是99%的情况下,你根本不需要修改一个类。

admin
版权声明:本站原创文章,由admin2016-07-06发表,共计7084字。
转载提示:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)