关于廖雪峰提到的元类的应用实例的解释
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(attrs) print('Found model: %s' % name) mappings = dict() for k, v in attrs.items(): if isinstance(v, Field): #这个有点难理解,为什么不是k,而是v print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v for k in mappings.keys(): attrs.pop(k) 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 k, v in self.__mappings__.items(): fields.append(v.name) params.append('?') args.append(getattr(self, k, 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='Michael', email='test@orm.org', password='my-pwd',sdfjojf='sdfe') u.save()
当创建实例时,需要调用User类,首先会检查有没有__metaclass__属性,找到了,通过metalclass创建类(基于User类,传入的name为User,父类为Model,属性为User类的属性)。之后在实例化过程中,需要调用创建的新的User类中的方法,如果没有,需要到父类(metalclass创建新类会传承User类的父类)中查找,比如说之后会调用__init__方法,之后又调用save方法。
结果为:
Found model: User Found mapping: email ==> <StringField:email> Found mapping: password ==> <StringField:password> Found mapping: id ==> <IntegerField:uid> Found mapping: name ==> <StringField:username> SQL: insert into User (password,email,username,id) values (?,?,?,?) ARGS: ['my-pwd', 'test@orm.org', 'Michael', 12345]
另外:__metaclass__函数中传递的是在__dict__中可以查询到的特殊类属性。
程序来源:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000
所以,我们加几行代码:
1 class Field(object): 2 3 def __init__(self, name, column_type): 4 self.name = name 5 self.column_type = column_type 6 7 def __str__(self): 8 return '<%s:%s' % (self.__class__.__name__, self.name) 9 10 class StringField(Field): 11 12 def __init__(self, name): 13 super(StringField, self).__init__(name, 'varchar(100)') 14 15 class IntegerField(Field): 16 17 def __init__(self, name): 18 super(IntegerField, self).__init__(name, 'bigint') 19 20 class ModelMetaclass(type): 21 22 def __new__(cls, name, bases, attrs): #传入的是类的属性 23 if name=='Model': 24 return type.__new__(cls, name, bases, attrs) 25 print(attrs) #后来加 26 print('Found model: %s' % name) 27 mappings = dict() 28 29 for k, v in attrs.items(): 30 if isinstance(v, Field):#这个有点难理解,为什么不是k,而是v 31 print('Found mapping: %s ==> %s' % (k, v)) #这里是将k赋值为id,name,email,password,所以type(k)为str 32 mappings[k] = v 33 for k in mappings.keys(): 34 attrs.pop(k) #删除一些属性 35 attrs['__mappings__'] = mappings # 保存属性和列的映射关系 36 attrs['__table__'] = name # 假设表名和类名一致 37 return type.__new__(cls, name, bases, attrs) 38 39 class Model(dict, metaclass=ModelMetaclass): 40 41 def __init__(self, **kw): 42 super(Model, self).__init__(**kw) 43 44 def __getattr__(self, key): 45 try: 46 return self[key] 47 except KeyError: 48 raise AttributeError(r"'Model' object has no attribute '%s'" % key) 49 50 def __setattr__(self, key, value): 51 self[key] = value 52 53 def save(self): 54 fields = [] 55 params = [] 56 args = [] 57 for k, v in self.__mappings__.items(): 58 fields.append(v.name) 59 params.append('?') 60 args.append(getattr(self, k, None)) 61 sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params)) 62 print('SQL: %s' % sql) 63 print('ARGS: %s' % str(args)) 64 65 class User(Model): 66 # 定义类的属性到列的映射: 67 id = IntegerField('id') 68 name = StringField('username') 69 email = StringField('email') 70 password = StringField('password') 71 72 73 print('...') 74 for i, v in User.__dict__.items(): #后来加 75 print(i, v) 76 print('...') 77 78 u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd',sdfjojf='sdfe') 79 u.save()
会得到下面的结果:
{'__module__': '__main__', '__qualname__': 'User', 'id': <__main__.IntegerField object at 0x0051FC50>, 'name': <__main__.StringField object at 0x0051FC70>, 'email': <__main__.StringField object at 0x0051FC90>, 'password': <__main__.StringField object at 0x0051FCB0>} Found model: User Found mapping: id ==> <IntegerField:id Found mapping: name ==> <StringField:username Found mapping: email ==> <StringField:email Found mapping: password ==> <StringField:password ... __module__ __main__ __mappings__ {'id': <__main__.IntegerField object at 0x0051FC50>, 'name': <__main__.StringField object at 0x0051FC70>, 'email': <__main__.StringField object at 0x0051FC90>, 'password': <__main__.StringField object at 0x0051FCB0>} __table__ User __doc__ None ... SQL: insert into User (id,username,email,password) values (?,?,?,?) ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
关于57行,类中有的属性(类中__dict__可以查到的属性),可以在定义的函数中使用可以通过self.属性 查询到!
比如:
class P(): def qss(self): print(self.__module__) print(dir(P)) print(P.__dict__) p=P() p.qss()
运行结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'qss'] {'__module__': '__main__', 'qss': <function P.qss at 0x00783810>, '__dict__': <attribute '__dict__' of 'P' objects>, '__weakref__': <attribute '__weakref__' of 'P' objects>, '__doc__': None} __main__
在元类中选择重新定义 __new__()
方法还是 __init__()
方法取决于你想怎样使用结果类。__new__()
方法在类创建之前被调用,通常用于通过某种方式(比如通过改变类字典的内容)修改类的定义。而 __init__()
方法是在类被创建之后被调用,当你需要完整构建类对象的时候会很有用。如果要用 super()
函数来搜索之前的定义。它只能在类的实例被创建之后,并且相应的方法解析顺序也已经被设置好了。