• Python元编程


    Python的第n任助手正式上岗了。 

    “老大,有程序员要创建对象,怎么办?”我向Python解释器发出了预警,上岗后头一次遇到这种情况,我有点紧张。。。

    1 class Person:
    2     def sayHello(self,name):
    3         print("hello,"+name)
    4 
    5 p = Person()
    6 
    7 p.sayHello("andy")

    “怕啥,我告诉你怎么做啊,首先找到Metaclass(元类),用元类来创建Class, 最后用Class对象来创建实例。”老大说着还给我画了个图:

            

     “不是吧!刚才还说人家Java鸡飞狗跳,我看我们这儿也丝毫不差(参见当创建对象时......),一个Class(如Person)在内存中用个对象来表示我理解,毕竟在我们的世界中,一切都是对象嘛, 但是这Metaclass(元类)是什么鬼?”   

    “是啊,类是一个对象,调用这个类对象的__new__方法就可以创建出这个类的实例。那么问题来了, 类对象是怎么来的?怎么把这个类对象给new 出来?”  老大没有回答,只是反问。  

    “不是程序员写的吗, class Person.....”  我有点底气不足。 

    “程序员写的只是代码,都是文本而已,我们在执行的过程中需要用Metaclass 把这个Person类对象给创建起来的。” 

    “可是我也没有看到Person类的Metaclass啊?! 他到底在哪儿?”  

    “那是你没有找到, Person类中没有,就去它的父类中去找,如果也没有,就继续向父类的父类去找,如果在任何父类中都找不到Metaclass,就去模块中去找,如果还是找不到,就用缺省的Metaclass,即type。” 

    我按照老大的要求,去找这个Metaclass,没有找到,只好用缺省的type了。

    可是我记得这个type不是个类,是个函数啊,可以用来查看一个变量的类型: 

    >>> type(1)
    <class 'int'>
    >>> type("aaa")
    <class 'str'>
    >>>

    老大说:“这个type啊,还有另外一个用法,可以创建其他类对象,在创建的时候,需要三个参数:”

    1. 要创建的类对象的名称,例如"Person" 

    2. 要创建的类对象的父类,例如(object,) 

    3. 包含属性的字典,即类的属性和方法。例如{"sayHello": sayHello} 

    比如,下面这段代码也创建了一个类对象Person,和程序员写的class Person...  效果是一样的。

     1 def sayHello(self,name):
     2     print("hello,"+name)
     3 
     4 #通过type来创建一个类对象,名称为Person,这个类对象有一个方法sayHello
     5 Person = type("Person",(),{"sayHello":sayHello})
     6 
     7 #通过类对象来创建实例
     8 p = Person()
     9 
    10 p.sayHello("andy")  # hello andy 

     嘿,这个办法不错啊,可以在运行时、动态地创建一个全新的类出来!隔壁的Java虽然也能做到,但是得利用ASM之类工具去直接操作字节码,太麻烦了,我大Python直接通过普普通通、简简单单的Python代码就搞定了!

    这就是动态脚本语言的一个优势吧! 

    之前听说过元编程,现在应该就是元编程了吧?但是这个Metaclass到底有什么用处呢? 程序员为什么不直接在代码中写class Person..... 这样的代码? 这样多直观啊。

    老大说:“有些程序员会自定义Metaclass,这些自定义的Metaclass 主要做这些事情:” 

    1. 拦截类的创建 

    2. 读取类的信息,可能做修改 

    3. 返回新的类。 

    拦截类的创建? 为什么有这样“变态”的需求?

    我真想看看一个自定义的Metaclass,看看它到底是怎么“变态”的。 

     

    没多久,机会来了,又要创建对象了。 

    1 from django.db import models
    2 
    3 class Employee(models.Model):
    4     name = models.CharField(maxlength = 50)    
    5     age  = models.IntegerField()
    6     #其他代码略#

     在Employee中没有看到Metaclass, 我就去父类Model中去寻找,运气不错,一下子就找到了metaclass ,叫做ModelBase:

    class Model(metaclass=ModelBase):
        #其他代码略

    赶紧去看ModelBase的代码,唉,实在是有点复杂了,让我看得头晕。 

    老大说:“你不用花费时间了,你的前任的前任曾经研究过它,是为了实现ORM !” 

    “ORM?” 

    “就是对象和关系数据库的映射。你想想,程序员创建的Python对象想要保存到数据库中,该怎么办?” 老大问道。 

    “那还不简单,程序员可以写SQL代码啊,insert into employee(name,age) values(?,?),其中包含那个Employee对象的name ,age的值不就行了?”

    那样就有点笨拙了,你再想想,能不能简化程序员的工作,别让他们去写这些烦人的、容易出错的SQL代码?能不能让框架来做这件事?” 老大写了两行代码。

    1 employee = Employee(name="andy",age=20)  
    2 employee.save()

    “看看,程序员只要把对象创建出来,调用下save方法就行了,SQL语句就会形成,保存到数据库中。”

    (注:这里略过了数据库连接的管理)

    “难道ModelBase这个元类在后面做‘手脚'?”我似乎有点理解了。

    “没错,你看到这些Employee类的属性没有? 就是程序员写的那些name, age...... 程序员这么写,其实就是在告诉ModelBase,尊敬的Metaclass啊, 这些都是数据库的列啊,列名是 name, 类型是char(50) , 还有个列名是age,是个整数。” 

     

    “那个MetaClass ,对,就是ModelBase会读取这些列名、类型,并且记录下来。 有了列名的信息,将来就可以形成insert, update,delete等SQL语句了。对不对?”

     原来如此!看来ModelBase在创建Employee类对象的时候,“偷偷地”读取了Employee类的定义信息,这样才能在背后实现ORM! 

     

    我按照老大的指示,调用ModelBase的__new__方法,创建了Employee类对象。 

    接下来又调用Employee类对象的__new__方法,创建了Employee实例对象。  

    1 employee = Employee(name="andy",age=20)  
    2 employee.save()

    当程序员调用employee.save()方法的时候,正如老大所说,神奇的魔法发生了,一条sql语句形成,并且发送给了数据库去执行。

    我感慨到:“这Python的元编程还是真是不错啊,能在运行时动态地修改类,比隔壁的Java强多了!”

    “Python元编程的技术不仅仅是Metaclass,还多着呢,你慢慢学吧!”

    本文参考自:深入浅出Python元编程

  • 相关阅读:
    SSL和SSH的差别
    cocos2d-x 3.0游戏实例学习笔记 《跑酷》 第五步--button控制主角Jump&amp;Crouch
    UVA
    程序员,你们这么拼是找不到妹纸的!
    组件:表行组件
    表单修饰符.lazy.number.trim
    表单下拉框select
    表单单选按钮input[type="radio"]
    表单复选框input[type="checkbox"]
    表单控件绑定v-model
  • 原文地址:https://www.cnblogs.com/qianyeliange/p/9669554.html
Copyright © 2020-2023  润新知