Python进阶——type与元类

前言

python的元类使用场景一般在大型框架里面,例如Django的ORM框架、基于python实现的高级设计模式,元类的这部分内容相对晦涩,但也是作为python非常核心的知识点,通过解析其机制,有利于阅读和学习优秀中间件源代码的设计逻辑,在面向对象设计的重要性不言而喻。本博客后面的内容将会给出较为复杂的设计模式的文章,里面会出现较多的元类编程,因此有必要单独开一篇文章讨论python元类,相关内容将参考Stack Overflow上一篇很受欢迎的关于python metaclasses的文章:what-are-metaclasses-in-python

1、python的class对象

1.1 python的class也是一种object

”在python的世界里,一切皆对象(object)“,这句话经常出现在很多python书籍中有关”面向对象或者类“文章里。如果你要深入python,首先面向对象的思维和面向对象的编程经历较为丰富。掌握对类的理解和运用,是理解元类的重要基础。
1.1 类即对象

In [1]: class Foo(object):
   ...:     pass
   ...:
In [2]: my_instance=Foo()
In [3]: print(my_instance)
<__main__.Foo object at 0x108561b00>

这里创建了一个名为Foo的类,打印它的实例,可以看到该实例是一个Foo object 存放在内存地址:0x108561b00
这里只是说明Foo类的实例是object,怎么确认Foo是一个object呢?
两种方式可以回答:
方式一:在定义阶段:Foo(object),Foo这个类继承object,所以Foo是object
方式二:

In [8]: isinstance(Foo,object)
Out[8]: True

既然Foo是一个object,那么对于该object则可以扩展其功能:

  • 可赋值给变量
  • 可被复制
  • 可添加属性
  • 可将其当作函数参数传递
  • 当然也可绑定新的类或者对象
In [13]: NewFoo=Foo
In [14]: print(NewFoo)
<class '__main__.Foo'>


In [16]: CloneFoo=copy.deepcopy(Foo)
In [17]: print(CloneFoo)
<class '__main__.Foo'>


In [20]: Foo.new_attr='bar'
In [21]: Foo.new_attr
Out[21]: 'bar


In [22]: def myfunc(obj):
 ...:     print(obj.__name__)
 ...:
In [23]: myfunc(Foo)
Foo


In [24]: class  NewFoo(object):
 ...:     pass
 ...:
In [25]: Foo.x=NewFoo
In [26]: Foo.x
Out[26]: __main__.NewFoo

总之,只要拿到一个object,你可以对其扩展任意你想得到效果

1.2 动态创建类

什么是动态创建类?只有运行这个程序后,通过判断给定参数来决定创建的是类A还是类B,而不是给程序写为固定生产类A。

In [28]: def choose_class(which):
    ...:     if which =='Foo':
    ...:         class Foo(object):
    ...:             pass
    ...:         return Foo
    ...:     elif which == 'Bar':
    ...:         class Bar(object):
    ...:             pass
    ...:         return Bar
    ...:
    ...:

In [29]: myclass=choose_class('Bar')
In [32]: print(myclass.__name__)
Bar

前面提到,既然Foo创建一个实例就是一个对象,把这个逻辑放在Foo身上:既然(某某某)创建一个对象就是一个Foo类,这个某某某是什么?可以做什么?
这个某某某就是type这个内建函数(函数也是一个对象),用type也可以像上面一样动态的创建一个类,用法:

type(要创建的类名,该类的所有父类名字组成的元组(若无父类,则为空元组),要创建该类需要用到入参:属性的字典)
一般写成:
type(class_name,class_bases,class_dict)

经典方式一般如下:

class Person(object):
    car='Model 3'
    def __init__(self,name,age):
        self.name=age
        self.age=age

    def info(self):
        print('my name is {} and {} years old'.format(self.name,self.age))

使用type函数动态创建以上Person类的过程:

# 因为Person继承object,所以type的第二个位置参数为(object,),Person类有三个属性因此class_dict为{'car':car,'__init__':__init__,'info':info})
car = 'Model 3'
def __init__(self,name,age):
    self.name = name
    self.age = age

def info(self):
    print(self.name,self.age)

Person = type('Person',(object,),{'car':car,'__init__':__init__,'info':info})


In [3]: Person.__dict__
Out[3]:
mappingproxy({'car': 'Model 3',
              '__init__': <function __main__.__init__(self, name, age)>,
              'info': <function __main__.info(self)>,
              '__module__': '__main__',
              '__dict__': <attribute '__dict__' of 'Person' objects>,
              '__weakref__': <attribute '__weakref__' of 'Person' objects>,
              '__doc__': None})
In [5]: person = Person('Watt',20)

In [6]: person
Out[6]: <__main__.Person at 0x10896f9e8>

type创建完整Person类!本章内容主要通过类的创建,因此type这个函数并用其实现动态创建类,为元类这个话题做了铺垫,通过以上type创建的实例推出,python创建类必须要具备以下三个参数:

  • 1、类名class_name
  • 2、继承关系class_bases
  • 3、类的名称空间class_dict
    这三个参数是揭开元类是如何改变类的秘密。

2 、Python的metaclass元类

2.1 认识type

前面的内容已经说明Python中的类也是对象,那么metaclass元类(元类自己也是对象)就是用来创建这些类的类,例如可以这样理解:

MyClass = MetaClass()    #元类创建了类
MyObject = MyClass()     #被元类创建的类后,用它创建了实例

在上一节内容,type可创建MyClass类:
MyClass = type('MyClass', (), {})
MyClass是type()这个特殊类的一个实例,只不过这个实例直接就是类。
以上的逻辑主要说明一件事:type这个特殊类,就是python的一个元类,type是Python在背后用来创建所有类的元类,这句话如何理解?
首先,还是那句熟悉的话:在python的世界里,一切皆对象(object),包括各类数据结构、函数、类以及元类,它们都来源于一个“创物者”,这个强大的创物者这就是type元类。
查看每种对象的__class__属性:

In [55]: num=10
In [56]: num.__class__
Out[56]: int

In [58]: alist=['a','c']
In [59]: alist.__class__
Out[59]: list


In [60]: def foo():
    ...:     pass
    ...:

In [61]: foo.__class__
Out[61]: function


In [62]: class Bar(object):
    ...:     pass
    ...:

In [64]: b=Bar()
In [65]: b.__class__
Out[65]: __main__.Bar

说明每个对象都是某种类,
那么,一个__class__.__class__又是属于哪种类呢?

In [69]: num.__class__.__class__
Out[69]: type

In [70]: foo.__class__.__class__
Out[70]: type

In [71]: alist.__class__.__class__
Out[71]: type

In [72]: b.__class__.__class__
Out[72]: type

在继续往“创物者”方向靠近,发现最后都是type:

In [74]: foo.__class__.__class__.__class__
Out[74]: type

In [75]: foo.__class__.__class__.__class__.__class__
Out[75]: type

以上说明:python的各类数据结构、函数、类以及元类,它们都来源于一个“创物者”,这个强大的创物者这就是type元类。

2.2 认识__metaclass__属性

在python的元类的设计中,通常会出现__metaclass__属性,一般用法如下:

class Foo(object):   #python2版本的写法
    __metaclass__ = something…

class Foo(metaclass=something):   #python3版本的写法
    __metaclass__ = something…

当一个类的内部属性定义了__metaclass__属性,说明这个类将由某个元类来创建,当Foo类一旦被调用,因为设计类时可能有继承关系,因此会出现属性搜索过程:
1)Foo的定义里面有__metaclass__这个属性?如果有,解释器在内存中通过something...这个元类创建一个名字为Foo的类(对象)
2)如果在Foo的作用域内未找到__metaclass__属性,则继续在父类中寻找,若在父类找到,则用something...这个元类创建一个名字为Foo的类(对象)。
3)如果任何父类中都找不到__metaclass__属性,它就会在模块层次中去寻找__metaclass__,若找到,则用something...这个元类创建一个名字为Foo的类(对象)。
4)如果还是找不到__metaclass__,解释器最终使用内置的type来创建这个Foo类对象。

从上面过程可知,既然找到something...这个元类后它就可以创建类,说明它与type这个终极元类作用一样:都是用来创建类。
所以可推出:__metaclass__指向某个跟type功能相仿的元类———任何封装type的元类、继承type的子类、type本身

下面用元类实现的redis连接单例来感受下以上的逻辑:

class RedisMetaSingleton(type):
    
    def __init__(cls,class_name,class_bases,class_dict):
        """元类做初始化"""
        #super(RedisMetaSingleton, cls).__init__(class_name, class_bases, class_dict) python2写法
        super().__init__(class_name, class_bases, class_dict) # python3写法
        cls._instance =None
        
    def __call__(cls,host,port,db):
        """call调用即完成类的实例化,用类的入参创建redis连接实例"""
        if not cls._instance:
            # cls._instance = super(RedisMetaSingleton, cls).__call__(host,port,db) python2写法
            cls._instance = super().__call__(host,port,db)# python3写法
        return cls._instance
        
class RedisSingleton(metaclass=RedisMetaSingleton):
    "redis操作专用类"
    def  __init__(self,host,port,db):
        self.host=host
        self.port=port
        self.db=db
    def conn(self):
    	pass

测试其实例是否为单例:

In [37]: r1=RedisSingleton('182.0.0.10','6379',0)
In [38]: r1
Out[38]: <__main__.RedisSingleton at 0x10fdc6080>

In [39]: r2=RedisSingleton('182.0.0.10','6379',0)
In [40]: r1 is r2
Out[40]: True

将单例逻辑放在定义元类这里,其他redis常用方法则放在子类实现。此外,该测试用例需要注意的两点:
1)RedisMetaSingleton的__init____call__第一个参数为cls,表示元类要创建的”类对象“,因此用cls而不是self。元类至于类对象(mataclass==>class object),就像类至于实例(class==>instance),反复理解该句。
2)__init__(cls,class_name,class_bases,class_dict),第2个到4个参数,其实就是type元类创建类的所需参数:
type(类名,父类元组(若无父类,则为空元组),类属性或内部方法的字典)
3)由于RedisMetaSingleton继承type,那么super(RedisMetaSingleton, cls)经过搜索后,父类就是type,因此
A: super(RedisMetaSingleton, cls).__init__(class_name, class_bases, class_dict)的初始化就等价于
type.__init__(class_name, class_bases, class_dict)的初始化
B:super(RedisMetaSingleton, cls).__call__(host,port,db)创建类对象就等价于type.__call__(host,port,db)创建类对象
这就说明RedisSingleton指定由RedisMetaSingleton来创建,在RedisMetaSingleton内部最后交由type.__init__初始化,证明过程如下:

In [119]: class RedisMetaSingleton(type):
     ...:     """在元类层面实现单例"""
     ...:     def __init__(cls,class_name,class_bases,class_dict):
     ...:         super(RedisMetaSingleton, cls).__init__(class_name, class_bases, class_dict)
     ...:         print('class_name:{} class_bases:{} class_dict:{}'.format(class_name,class_bases,class_dict))
     ...:         cls.cls_object =None
     ...:
     ...:     def __call__(cls,host,port,db):
     ...:         if not cls.cls_object:
     ...:             cls.cls_object = super(RedisMetaSingleton, cls).__call__(host,port,db)
     ...:         return cls.cls_object
     ...:
     ...: class RedisSingleton(metaclass=RedisMetaSingleton):
     ...:     "redis操作专用类"
     ...:     def  __init__(self,host,port,db):
     ...:         self.host=host
     ...:         self.port=port
     ...:         self.db=db
     ...:     def conn(self):
     ...:         pass
     ...:

当以上代码在ipython解释器敲下去后,解释器对RedisMetaSingleton做了__init__初始化工作,故可得到以下打印信息

class_name:RedisSingleton  # 类名
class_bases:() # 父类元组
class_dict:{'__module__': '__main__', '__qualname__': 'RedisSingleton', '__doc__': 'redis操作专用类', '__init__': <function RedisSingleton.__init__ at 0x10ffa0158>, 'conn': <function RedisSingleton.conn at 0x10ffa00d0>} # 要创建类的所有属性字典

这三个参数就是type创建类的所需的参数:
type(类名,父类元组(若无父类,则为空元组),类属性或内部方法的字典)

以上内容略显复杂:归结起来,只要一个普通类指定需要元类创建,那么最终一定是由type这个终极元类来创建。

2.3 自定义元类

对元类的构建和原理有一定认识后,那么可通过元类定制普通类,真正站在创物者的上帝视野来创建普通类。
现在有这样一个需求,要求创建的普通类的属性满足以下条件:
对于开头不是__的属性,都要大写,例如get_name(self),在元类创建该普通类后都会被改为GET_NAME(self)
开头为__的属性,大小写保持不变。
从type创建普通类的“公式“可知:type(class_name,class_bases,class_dict),class_dict就是放置了普通类属性或内部方法的字典),故只需要对其修改后,再重新传入type即可实现,需要基于type的__new__方法实现,type当然有__new__方法,因为type是元类,也是类。(元类必然有__new__方法,它创建的普通类例如Person、RedisConn才有这个内建的__new__方法。)
具体实现:

class UpperAttrMetaclass(type):
    # 在这里,被创建的对象是类,因此第一个参数为cls,而不是类实例化的self,且需重写__new__方法
    def __new__(cls, class_name,class_bases,class_dict):
        uppercase_attr = {}
        for name, val in class_dict.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val
        # 用uppercase_attr替换了原class_dict,再传入到type,由type创建类,实现了自定义创建类的目标      
        return type(class_name,class_bases, uppercase_attr)

考虑到return type(class_name,class_bases, uppercase_attr)的写法不是pythone的OOP的写法(不够高级、抽象),因此又转化为以下OOP写法:

class UpperAttrMetaclass(type):
    # 在这里,被创建的对象是类,因此第一个参数为cls,而不是实例self,且需重写__new__方法
    def __new__(cls, class_name,class_bases,class_dict):
        attrs = ((name, value) for name, value in class_dict.items() if not name.startswith('__'))
        uppercase_attr  = dict((name.upper(), value) for name, value in attrs)
        # 用uppercase_attr替换了原class_dict,再传入type,由type创建类,实现了自定义创建类的目标      
        return type.__new__(cls,class_name,class_bases, uppercase_attr)

以上OOP风格在知名的python框架中到处可见!此外,我们知道通过super(UpperAttrMetaclass,cls)可以搜索到父类type,因此开发者会习惯写成以下形式:

class UpperAttrMetaclass(type):
    def __new__(cls, class_name,class_bases,class_dict):
        attrs = ((name, value) for name, value in class_dict.items() if not name.startswith('__'))
        uppercase_attr  = dict((name.upper(), value) for name, value in attrs)  
        # return super(UpperAttrMetaclass,cls).__new__(cls,class_name,class_bases, uppercase_attr)
        return super().__new__(cls,class_name,class_bases, uppercase_attr) # python3的写法

定义一个普通类测试:

In [124]: class Person(metaclass=UpperAttrMetaclass):
     ...:     def __init__(self,name,age):
     ...:         self.name=name
     ...:         self.age=age
     ...:     def get_name(self):
     ...:         print('name is:',self.name)
     ...:     def get_age(self):
     ...:         print('age is:',self.age)
     ...:

In [125]: Person.__dict__
Out[125]:
mappingproxy({'GET_NAME': <function __main__.Person.get_name(self)>,
              'GET_AGE': <function __main__.Person.get_age(self)>,
              '__module__': '__main__',
              '__dict__': <attribute '__dict__' of 'Person' objects>,
              '__weakref__': <attribute '__weakref__' of 'Person' objects>,
              '__doc__': None})
	

当普通类Person定义后,解释器已经用元类UpperAttrMetaclass创建了Person普通类,其get_name和get_age方法名都被改为大写:GET_NAME和GET_AGE,其他双下划线的的方法名字保持不变。

此外,metaclass不局限于类的调用,也可以在任何对象内部调用,例如函数内部调用,例如以下一个模块upper_attr_by_func.py:

def upper_attr(class_name,class_bases,class_dict):
    uppercase_attr = {}
    for name, val in class_dict.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    return type(class_name,class_bases,class_dict)

__metaclass__ = upper_attr  # 该元类只能作用在本模块的所有类,对其他模块a.py、b.py无影响。

class Person(metaclass=UpperAttrMetaclass):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def get_name(self):
        print('name is:',self.name)

    def get_age(self):
        print('age is:',self.age)

但需要注意的是:这种方式,元类的作用域将受到限制,仅能影响本模块upper_attr_by_func.py的所有类,对其他模块的类不产生作用。

综上,可总结元类定制普通类的创建一般如下过程:

  • 拦截一个普通类,一般在会使用__new__,__init__ 和 __call__,这些方法的内部可以放入对普通类进行不同定制的代码逻辑,其中:
    A、__new____init__方法用于控制类的行为
    B、 __call__方法用于控制类实例化的行为(

  • 修改普通类,一般是指修改type(class_name,class_bases,class_dict)里面的三个参数,尤其对class_dict修改频繁,例如要求class_dict里面的必须要有__doc__属性,甚至针对父类元组class_bases来操作其继承关系。

  • 通过return super().__new__(cls,class_name,custom_bases, custom_class_dict)创建并返回普通类

元类一般用于复杂的框架上改变类的行为,对于普通简单的类,还有其他两种手段用来改变类:

  • monkey patching
  • 类装饰器
    按Stack Overflow上的”建议“:
    如果需要改变类,99%的情况下使用这两种方法,但其实98%的情况你根本不需要改变类。所以你看到很多较为简单的python轮子,一般是几个普通类就可以完成,根本无需动用元类来构建普通类。

3、元类定制普通类的示例——myORM

本节内容参考了廖雪峰的文章,但其文章有很多关键的语句并无做更细致的说明,本节内容会在重要的元类实现逻辑上给出更详细的文字说明。
这里将实现一个轻量ORM——myORM:
在架构层面(不面向用户)

  • 架构定义了一个元类ModelMetaClass,用于拦截和修改普通类User定义阶段的class_dict属性字典,并用改造后的class_dict传入type来创建普通类User对象。
  • 架构定义了一个Model类,用于把字段属性名和字段值封装在拼接的SQL语句,主要负责与数据库的增删查改。
  • 架构定义了一个基本字段类Field:包含字段名和字段类型

在用户层面(面向用户,用户可自行定义各种模型)

  • 用户定义一个整数字段类IntField,用于存放整型类型数据,例如id号,age
  • 用户定义一个字符串字段类CharField,用于存放字符类型数据,例如name,email
  • 创建一个User模型的一条行记录

class ModelMetaClass(type):
    """
    例如定义User普通类,如下:
    class User(Model):
        id=IntField('user_id')
        name=CharField('user_name')
        email=CharField('email')
        password=CharField('password')

    那么元类ModelMetaClass捕获到的属性字典为: 
    class_dict={
        '__module__': '__main__', 
        '__qualname__': 'User',
        'id':IntField<user_id,bigint>,
        'name':CharField<'user_name',varchar(100)>,
        'email':CharField<'email',varchar(100)),
        'password':CharField<'password',varchar(100)>
    }
    """
    
    def __new__(cls,class_name,class_bases,class_dict):
        if class_name == "Model":
            # 如果创建的普通类为Model,不修改该类,直接创建即可
            return type.__new__(cls,class_name,class_bases,class_dict)
        print('在ModelMetaClass元类捕获到普通类的属性字典:\n',class_dict)
        
        # 用于存放字段类型的属性
        fields_dict=dict()

        for attr_name,attr in class_dict.items():
            # 因为普通类属性字典还有__doc__,__qualname__等属性,因此要过滤出属于Field类型属性
            if isinstance(attr,Field):
                # 打印结果:attr_name is "id",field object is "<class '__main__.IntField'>"等字段信息
                print('attr_name is "{}",field object is "{}"'.format(attr_name,type(attr)))
                fields_dict[attr_name]=attr
            

        for field_name in fields_dict.keys():
            """
            把Field类型的属性在原属性字典剔除:
            u=User(name='Wott')
            print(u.name)# 打印结果Wott

            若不在原字典删除,那么u.name的值为:CharField<email,varchar(100)>
            这个值是元类创建User类的属性值,它会覆盖了值为'Wott'的u.name实例属性,显然不符合实际情况。
            """
            class_dict.pop(field_name)

        # 在原属性字典里增加一个私有属性(字典类型),这个私有属性保存了要创建字段的信息
        class_dict['__fields_dict__']=fields_dict



        """
        获取自定义模型中,指定Meta信息的数据库表名 
        class User(Model):
            ...
            class Meta:
                db_table=USER_T
        """
        attr_meta=class_dict.get('Meta',None)
        print('User模型定义的Meta属性:',attr_meta) # Meta: <class '__main__.User.Meta'>
        meta_table_name=getattr(attr_meta,'db_table',None)
        print('User模型在Meta指定的数据库表名为:',meta_table_name) # User模型在Meta指定的数据库表名为:: USER_T
        if attr_meta and meta_table_name:
                table=meta_table_name
                del class_dict['Meta']
        else:
                table=class_name # 若User模型没指定Meta中的数据库表名,则默认用User模型类名作为数据库表名
        class_dict['__table_name__']=table 
        
        # 把原Meta属性变成私有属性,这样创建出来的类更具OOP风格
        class_dict['__Meta__']=attr_meta
        # 以上完成对普通User模型的属性字典改造后,再重新把它传入到type,从而元类ModelMetaClass完成拦截=>定制=>创建普通类的过程。
        return type.__new__(cls,class_name,class_bases,class_dict)


class Model(dict,metaclass=ModelMetaClass):
    """
    1、Model类继承dict,目的是为了满足ORM中使用字典赋值和取值的方式例如
    创建u=User(id=1,name='Wott',email='[email protected]',password='1213')
	2、Model内部通过拼接普通类的字段属性信息,封装了原生sql语句,例如save(),filter(),update()等方法
    """
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def __getattr__(self,attr_name):
        """
        重写内建getattr方法,可实现类似u.name这种点号获取属性值得方式,用起来更具ORM风格
        """
        try:
            return self[attr_name]
        except KeyError:
            raise AttributeError("Model object has no attribute {}".format(attr_name))
    
    def __setattr__(self,col_name,col_value):
        """
        重写setattr方法,可实现类似u.name="Foo"这种通过点号设置属性值的方式,用起来更具ORM风格
        """
        self[col_name]=col_value
    
    def save(self):
        # 字段名列表
        column_name_list=[]
        place_holder_list=[]
        # 字段值列表
        column_value_list=[]
        fields_dict=self.__fields_dict__
        
        # 存放字段的字典,key就是字段名,放在字段名列表,value就是字段值,放在字段值列表,两个列表用于拼接sql语句
        for attr_name,attr in fields_dict.items():
            # 打印为:attr_name==>id,attr==><class '__main__.IntField'>,attr.col_name==>user_id
            column_name_list.append(attr.col_name)
            place_holder_list.append('%s')
            print(self) # Model Dict:{'id': 1, 'name': 'Foo', 'email': '[email protected]', 'password': 'Pa33Wood'} 
            column_value_list.append(self[attr_name])
            # 或者column_value_list.append(getattr(self,attr_name))

            sql = 'insert into %s (%s) values (%s)' % (self.__table_name__, ','.join(column_name_list), ','.join(place_holder_list))
        print('SQL语句:',sql)
        print('SQL的入参值列表:',column_value_list)
            # 连接mysql数据库后,使用cur.execute(sql,column_value_list)即可存入数据

    def __str__(self):
        return 'Model type:{}'.format(super().__str__())


class Field(object):
    def __init__(self,col_name,col_type):
        self.col_name=col_name # 字段名
        self.col_type=col_type # 字段类型

    def __str__(self):
        """
        例如将会打印以下格式:
        'id': IntField<user_id,bigint>等
        """
        return "{}<{},{}>".format(self.__class__.__name__,self.col_name,self.col_type)
    
    __repr__=__str__

class CharField(Field):
    """
    定义字符型字段,默认可变字符类型长度为100
    """
    def __init__(self, col_name, max_length=100):
        varchar_type="varchar({})".format(max_length)
        super().__init__(col_name, varchar_type)
    

class IntField(Field):
    """
    定义整型字段
    """
    def __init__(self, col_name, col_type="bigint"):
        super().__init__(col_name, col_type)

ModelMetaClass负责顶层设计(改造),用户创建所有的普通类如User、Article、Department等,都会被该元类重设设计(改造它们的class_dict)后再创建出这些普通类。

用户定义了一个User模型,有四个字段,并指定创建为表名为USER_T

class User(Model):
    id=IntField('user_id')
    name=CharField('user_name')
    email=CharField('email',max_length=200)
    password=CharField('password')

    class Meta:
        """
        自定义数据库表名,这里虽然Meta定义为类,
        但在元类ModelMetaClass的视角来看,它是一个属性,放在class_dict里面
        """
        db_table='USER_T'

当用户定义完以上的普通类User后,Python解释器首先在当前类User的定义中查找metaclass,显然当前上下文环境没有找到,则继续在父类Model中查找metaclass,发现Model定义了metaclass=ModelMetaClass,故直接交由ModelMetaclass来创建该普通的User类。

用户创建了User实例并尝试向db插入该条数据

u=User(id=1,name='Wott',email='[email protected]',password='1213') # Model继承dict,因此Model子类User当然可用字典创建方式来创建实例
u['name']='Foo'# Model继承dict,因此Model子类User当然可使用字典方式赋值
u.password='Pa33Wood'# Model内部定义__setattr__方法,故可用点号给属性赋值
print(u.email) # Model内部定义__getattr__方法,故可用点号取属性值
u.save()

以上代码各个位置上的print输出结果如下

第一句print输出:
在ModelMetaClass元类捕获到普通类的属性字典:
 {'__module__': '__main__', '__qualname__': 'User', 'id': IntField<user_id,bigint>, 'name': CharField<user_name,varchar(100)>, 'email': CharField<email,varchar(200)>, 'password': CharField<password,varchar(100)>, 'Meta': <class '__main__.User.Meta'>}

第二句print输出:
attr_name is "id",field object is "<class '__main__.IntField'>"
attr_name is "name",field object is "<class '__main__.CharField'>"
attr_name is "email",field object is "<class '__main__.CharField'>"
attr_name is "password",field object is "<class '__main__.CharField'>"

第三句print输出:
User模型定义的Meta属性: <class '__main__.User.Meta'>

第四句print输出:
User模型在Meta指定的数据库表名为: USER_T

第五句print输出:
[email protected]

第六句print输出:
Model type:{'id': 1, 'name': 'Foo', 'email': '[email protected]', 'password': 'Pa33Wood'}
Model type:{'id': 1, 'name': 'Foo', 'email': '[email protected]', 'password': 'Pa33Wood'}
Model type:{'id': 1, 'name': 'Foo', 'email': '[email protected]', 'password': 'Pa33Wood'}
Model type:{'id': 1, 'name': 'Foo', 'email': '[email protected]', 'password': 'Pa33Wood'}

第七句print输出:
SQL语句: insert into USER_T (user_id,user_name,email,password) values (%s,%s,%s,%s)

第八句print输出:
SQL的入参值列表: [1, 'Foo', '[email protected]', 'Pa33Wood']
发布了52 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/pysense/article/details/103516859