1 # 一张表一个类,表内每一行就是一个实例 2 ''' 3 一个单独的元类使用的程序分析。 4 ''' 5 6 class Field(object): 7 def __init__(self, name, column_type): 8 self.name = name 9 self.column_type = column_type 10 11 def __str__(self): 12 return '(<%s:%s>)' % (self.__class__.__name__, self.name) 13 14 15 # self.__class__:得到当前实例的类 self.__class__.__name__:得到当前实例的类的名字 16 # self.name:实例名字 17 18 __repr__ = __str__ 19 20 21 class StringField(Field): 22 def __init__(self, name): 23 super(StringField, self).__init__(name, 'varchar(100)') 24 25 26 class IntegerField(Field): 27 def __init__(self, name): 28 super(IntegerField, self).__init__(name, 'bigint') 29 30 31 # ModelMetaclass这个元类的__new__()方法在本程序中的作用: 32 # 1.在依据ModelMetaclass创建Model时,由Model向元类传入的参数为:cls:<class '__main__.ModelMetaclass>;name:Model;bases()传入的bases是一个tuple:(<class 'dict'>,),attrs是一个由Model类中的所有的方法名和方法函数地址组成的dict. 33 # 然后再传入该元类后,执行__new__(),由于name=='Model'所以返回由type('Model', (dict,), attrs)所组成的类。return了,不往下执行。 34 # 2.在准备创建User类时,可以见class User上的注释。此时name==User。在__new__()内执行时。if失效。往后运行,创建一个名为mappings的dict。mappings的作用是用来存储此时User里面的4个属性(id,name,password,email).并将作为一个属性返回。 35 # 由于attrs包含了User中的所有的属性。所以先要用isinstance(v,Field)判断是不是id/name/password/email中的一个。若找到,则复制如mappings中。 36 # 删除User的4个id/name/password/email属性(防止造成实例的属性遮盖类的同名属性)。 37 # 添加两个属性__mappings__和__table__。并把最后的attrs作为最后要生成的名为User的类的所有方法。 38 39 40 class ModelMetaclass(type): 41 def __new__(cls, name, bases, attrs): 42 # print('hello',cls,'---',name,'---',bases,'---',attrs) 43 if name == 'Model': # 如果名字==Model。那么久用type创建一个类。在这里,name:要创建的类的名字;bases:要创建的类的父类;attrs:类的方法与函数对照字典。 44 # print(attrs) 45 return type.__new__(cls, name, bases, attrs) 46 # return super().__new__(cls, name, bases, attrs) 47 print('Found model:%s' % name) 48 mappings = dict() 49 for k, v in attrs.items(): 50 if isinstance(v, Field): 51 print('Found mapping: %s==>%s' % (k, v)) 52 mappings[k] = v 53 for k in mappings.keys(): 54 # print([(k, v) for k, v in attrs.items() if isinstance(v, Field)]) 55 attrs.pop(k) 56 attrs['__mappings__'] = mappings 57 attrs['__table__'] = name 58 # print(attrs) 59 return type.__new__(cls, name, bases, attrs) 60 61 62 class Model(dict, metaclass=ModelMetaclass): 63 def __init__(self, **kw): 64 # print(Model.mro()) 65 # 调用父类dict类的__init__方法,需要传入一个dict。 66 super(Model, self).__init__(**kw) 67 68 def __getattr__(self, key): 69 try: 70 return self[key] 71 except KeyError: 72 raise AttributeError(r"'Model' object has no attribute '%s'" % key) 73 74 def __setattr__(self, key, value): 75 self[key] = value 76 77 def save(self): 78 fields = [] 79 params = [] 80 args = [] 81 for k, v in self.__mappings__.items(): 82 # print(v.name) 83 fields.append(v.name) 84 params.append('?') 85 # 当u=User(......)后,u就具有了4个属性u.id=12345 u.name='Michael' u.email='test@orm.org' u.password='my-pwd' 86 args.append(getattr(self, k, None)) 87 sql = 'insert into %s (%s) values (%s)' % (self.__table__, 88 ','.join(fields), 89 ','.join(params)) 90 # join()函数 91 # 语法: 'sep'.join(seq) 该例为:','.join(fields) 对应的:sep=, seq=fields. 92 # 参数说明 93 # sep:分隔符。可以为空 94 # seq:要连接的元素序列、字符串、元组、字典 95 # 上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串 96 # 返回值:返回一个以分隔符sep连接各个元素后生成的字符串 97 print('SQL: %s' % sql) 98 # print(args) 99 print('ARGS: %s' % str(args)) 100 101 102 # 当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到, 103 # 就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclass的ModelMetaclass来创建User类, 104 # 也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。 105 106 107 # 在创建User时,程序会先执行完里面的4条属性声明语句。然后再将包含该4条属性和所有的方法组成一个dict。传入ModelMetaclass的__new__中。 108 109 110 # 通过使用元类,程序员可以在这里自定义一个类(数据库的一张表)需要输入哪些元素。 111 112 class User(Model): 113 id = IntegerField('id') 114 name = StringField('username') 115 email = StringField('email') 116 password = StringField('password') 117 118 119 # 在这里时,User类里面没有4个单独的id/name/password/email属性,只有__mappings__和__table__属性。 120 121 # print(type(User.id)) 122 # print(dir(User)) 123 u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') 124 # print(u.name.name) 125 # print(u.id.name) 126 # print(u.email.name) 127 # print(u.password.name) 128 # print(dir(u.id)) 129 # print(u.id.name) 130 u.save() 131 # print(dir(Model))