一周一个Python语法糖:(三) 元类

先来了解下python魔法的内核吧:

一切皆对象

  • 一切皆对象
  • 一切皆有类型
  • “class” and "type" 之间本质上上并无不同
  • 类也是对象
  • 他们的类型都是type
class ObjectCreator(object):
        pass

这段代码将在内存中创建一个对象,名字叫ObjectCreator.

这个对象(类)自身拥有创建对象的(类实例的能力),##

而这也就是它为什么是一个类的原因##

你可以对该对象进行以下操作:

  1. 将它赋值给一个变量
  2. 可以拷贝它
  3. 可以为它增加属性
  4. 可以将其作为函数参数进行传递
#交互型
>>> def echo(o):
...     print(o)
... 
>>> echo(my_object)
<__main__.ObjectCreator object at 0x7fcf7d0d6c50>  #你可以将类做为参数传给函数
>>> ObjectCreator.new_attrituer=echo    #  你可以为类增加属性
>>> print(hasattr(ObjectCreator,'new_attrituer'))
True
>>> print(ObjectCreator.new_attrituer) #新加的类的方法属性

一:用type创建一个类

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
#三个参数的类型分别是str,tuple,dic

比如:

#目标类:
class Animal(object):
    def __init__(self,name):
        self.name=name
    def eat(self):
        pass
    def go_to_eat(self):
        pass
#用type创建
def init(self,name):
    self.name=name
def eat(self):
    pass
def go_to_eat(self):
    pass
Animal=type('Animal',(object,),{
    '__init__':init,
    'eat':eat,
    'go_to_eat':go_to_eat
})

>>>print(Animal)

这样我们实现了用type去动态创建一个类的
BUT~这样是不是太麻烦了!!!(抬走下一位!)

二:metaclass登场

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

# metaclass是创建类,所以必须从`type`类型派生:
class Listmetaclass(type):
    def __new__(cls,name,bases,attrs):
        attrs['add']=lambda self,value:self.append(value)
        return type.__new__(cls,name,bases,attrs)
    
class Mylist(list):
    __metaclass__=Listmetaclass

  • 按照默认习惯.metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass
  • 当我们写下__metaclass__=Listmetaclass时候,它表示解释器在创建Mylist的时候
    ,要通过Listmetaclass的__new__()方法创建
    在此,我们可以修改类的定义,比如:加上新的方法,然后返回修改后的定义.
  • __new__()方法接受的参数依次是:
    1. 当前准备创建的类的对象
    2. 类的名字
    3. 类继承的父类的集合
    4. 类的方法的集合

元类的主要目的就是为了当创建类时能够自动改变类.

使用到元类的代码比较复杂,这背后的原因倒并不是因为元类本身,

而是因为你通常会使用元类去做一些晦涩的事情,

依赖于自省,控制继承等等。

确实,用元类来搞些“黑暗魔法”是特别有用的,

因而会搞出些复杂的东西来。

就这个例子来说:

  • 拦截类的创建(拦截Mylist的创建)
  • 修改类(给Mylist增加add的方法)
  • 返回修改之后的类

**“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” **-----Tim Peters

元类的主要作用的创建API,一个典型的例子是 ORM.

ORM全称“Object Relational Mapping”,
即对象-关系映射,就是把关系数据库的一行映射为一个对象,
也就是一个类对应一个表,
这样,写代码更简单,不用直接操作SQL语句。

~

要编写一个ORM框架,所有的类都只能动态定义,
因为只有使用者才能根据表的结构定义出对应的类来。

我们来尝试写一个简单的ORM框架吧
首先我们要知道使用者会调用什么接口:
比如,使用者可能会定义一个User类来操作数据表User.

class User(Model):
  #创建User表
  #创建四列属性
    id=IntegerField('id')
    name=StringField('username')
    email=StringField('email')
    password=StringField('password')
    
# 创建一个实例:
u=User(id='12345',name='zhou',email='[email protected]',password='123455')
#保存到数据库
u.save()  

首先,我们来定义Field类,它负责保存数据库表的字段名字跟字段类型:

class Field(object):
    def __init__(self,name,column_type):
        self.name=name
        self.column_type=column_type
    def __str__(self):
        return '<%s:%s>' %(self.__class__.__name__,self.name)

在Field的基础上,我们来定义各种Field:

#为了简化,我们先定义好各种属性的type
class StringField(Field):
    def __init__(self,name):
        super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
    def __init__(self,name):
        super(IntegerField,self).__init__(name,'bigint')

接下来,我们要编写元类了

class ModelMetaclass(type):
    def __new__(cls,name,bases,attrs):
        #如果是基类,直接返回(即不对model类进行修改)
        if  name=='Model':
            return type.__new__(cls,name,bases,attrs)
        print('Found model: %s' %name)
        #如果是其他类,则进行装饰
        #取出所有类属性,将其放入mapping
        mappings=dict()
        for key,value in attrs.iteritems():
            if isinstance(value,Field):
                print('Found mapping: %s==>%s' %(key,value))
                mappings[key]=value
        for key in mappings.iterkeys():
            attrs.pop(key)
        attrs['__mappings__']=mappings
        attrs['__table__']=name
        return type.__new__(cls,name,bases,attrs)
        

编写基类:

class Model(dict):
    __metaclass__=ModelMetaclass
    
    def __init__(self,**kw):
        super(Model,self).__init__(**kw)
        
    def __getattr__(self,key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no Attribute '%s'" %key)
    def __setattr__(self,key,value):
        self[key]=value
        
    def save(self):
        fields=[]
        params=[]
        args=[]
        for key,value in self.__mappings__.iteritems():
            fields.append(value.name)
            params.append('?')
            args.append(getattr(self,key,None))
        sql='insert into %s(%s) values(%s)' %(self.__table__,','.join(fields),','.join(params))
        print('SQL:%s' %sql)
        print('ARGS:%s' %str(args))

当用户自定义一个class User(Model)时候,
解释器首先在当前类User的定义中寻找
metaclass,如果没找到,就在继承的父类中继续找.,
找到了,就用model中的定义的ModelMetaclass去创建User类
也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。

在ModelMetaclass中做的事情:

  • 排除对Model 的修改
  • 在当前类 中查找定义的类的属性,如果找到一个FIeld属性,就保存到__mappings__字典中
    同时从类属性中删除Field属性,否则容易造成运行时的错误
  • 把表名保存到__table__中

在Model 类中,就可以定义各种操作数据库的方法了(列属性保存在__mappings__)中

全部代码:

#!/usr/bin/python3
class Field(object):
    def __init__(self,name,column_type):
        self.name=name
        self.column_type=column_type
    def __str__(self):
        return '<%s:%s>' %(self.__class__.__name__,self.name)

class StringField(Field):
    def __init__(self,name):
        super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
    def __init__(self,name):
        super(IntegerField,self).__init__(name,'bigint')
class ModelMetaclass(type):
    def __new__(cls,name,bases,attrs):
        if  name=='Model':
            return type.__new__(cls,name,bases,attrs)
        print('Found model: %s' %name)
        mappings=dict()
        for key,value in attrs.iteritems():
            if isinstance(value,Field):
                print('Found mapping: %s==>%s' %(key,value))
                mappings[key]=value
        for key in mappings.iterkeys():
            attrs.pop(key)
        attrs['__mappings__']=mappings
        attrs['__table__']=name
        return type.__new__(cls,name,bases,attrs)
        
class Model(dict):
    __metaclass__=ModelMetaclass
    
    def __init__(self,**kw):
        super(Model,self).__init__(**kw)
        
    def __getattr__(self,key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no Attribute '%s'" %key)
    def __setattr__(self,key,value):
        self[key]=value
        
    def save(self):
        fields=[]
        params=[]
        args=[]
        for key,value in self.__mappings__.iteritems():
            fields.append(value.name)
            params.append('?')
            args.append(getattr(self,key,None))
        sql='insert into %s(%s) values(%s)' %(self.__table__,','.join(fields),','.join(params))
        print('SQL:%s' %sql)
        print('ARGS:%s' %str(args))



class User(Model):
    id=IntegerField('id')
    name=StringField('username')
    email=StringField('email')
    password=StringField('password')
    
u=User(id='12345',name='zhou',email='[email protected]',password='123455')
u.save()  
       

运行结果:

Found model: User
Found mapping: email==>
Found mapping: password==>
Found mapping: id==>
Found mapping: name==>
SQL:insert into User(password,email,username,id) values(?,?,?,?)
ARGS:['123455', '[email protected]', 'zhou', '12345']

最后说几句:

  • 元类(Metaclass)类似于装饰器,可以在类的创建时动态修改类.

  • 元类语法糖:__metaclass__

学习参考:
廖雪峰Python教程
深入理解元类
5分钟理解元类

你可能感兴趣的:(一周一个Python语法糖:(三) 元类)