1. 程式人生 > >python——type()、metaclass元類和精簡ORM框架

python——type()、metaclass元類和精簡ORM框架

定制 定義 attribute varchar elm cep 實例 編寫代碼 __main__

1、type()函數

#type()函數既可以返回一個對象的類型,又可以創建出新的類型,  
# 比如,我們可以通過type()函數創建出Hello類,而無需通過class Hello(object)...的定義:  
  
def fn(self,name=‘world‘):  #先定義函數  
    print(‘Hello, %s.‘%name)  
#開始用type()  
hello = type(‘Hello‘,(object,),dict(hello=fn))  # 創建Hello class  
#要創建一個class對象,type()函數依次傳入3個參數:  
#class的名稱;  
#繼承的父類集合,註意Python支持多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;  
#class的方法名稱與函數綁定,這裏我們把函數fn綁定到方法名hello上。 

 

#type()函數既可以返回一個對象的類型,又可以創建出新的類型,
# 比如,我們可以通過type()函數創建出Hello類,而無需通過class Hello(object)...的定義:

def fn(self,name=‘world‘):  #先定義函數
    print(‘Hello, %s.‘%name)
#開始用type()
hello = type(‘Hello‘,(object,),dict(hello=fn))  # 創建Hello class
#要創建一個class對象,type()函數依次傳入3個參數:
#class的名稱;
#繼承的父類集合,註意Python支持多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
#class的方法名稱與函數綁定,這裏我們把函數fn綁定到方法名hello上。

  

if __name__ == ‘__main__‘:  
    h = hello()  
    h.hello()  
    print(type(hello))  
    print(type(h))  

if __name__ == ‘__main__‘:
h = hello()
h.hello()
print(type(hello))
print(type(h))

Hello, world.  
<class ‘type‘>  
<class ‘__main__.Hello‘>  

Hello, world.
<class ‘type‘>
<class ‘__main__.Hello‘>

2、metaclass元類

#metaclass   元類   metaclass允許你創建類或者修改類  
class Listmetaclass(type):  
    def __new__(cls, name,bases,attrs):  
        attrs[‘add‘] = lambda self,value:self.append(value) #增加了add()方法  
        return type.__new__(cls,name,bases,attrs)  
class MyList(list,metaclass=Listmetaclass):  
    pass  
#__new__()方法接收到的參數依次是:  
  
#1、當前準備創建的類的對象;  
#2、類的名字;  
#3、類繼承的父類集合;  
#4、類的方法集合。  
  
  
#元類一般情況不常用,但總會遇到需要通過metaclass修改類定義的。ORM就是一個典型的例子。  
#ORM全稱“Object Relational Mapping”,即對象-關系映射,就是把關系數據庫的一行映射為一個對象,  
# 也就是一個類對應一個表,這樣,寫代碼更簡單,不用直接操作SQL語句。  
#要編寫一個ORM框架,所有的類都只能動態定義,因為只有使用者才能根據表的結構定義出對應的類來。  

  

#metaclass   元類   metaclass允許你創建類或者修改類
class Listmetaclass(type):
    def __new__(cls, name,bases,attrs):
        attrs[‘add‘] = lambda self,value:self.append(value) #增加了add()方法
        return type.__new__(cls,name,bases,attrs)
class MyList(list,metaclass=Listmetaclass):
    pass
#__new__()方法接收到的參數依次是:

#1、當前準備創建的類的對象;
#2、類的名字;
#3、類繼承的父類集合;
#4、類的方法集合。


#元類一般情況不常用,但總會遇到需要通過metaclass修改類定義的。ORM就是一個典型的例子。
#ORM全稱“Object Relational Mapping”,即對象-關系映射,就是把關系數據庫的一行映射為一個對象,
# 也就是一個類對應一個表,這樣,寫代碼更簡單,不用直接操作SQL語句。
#要編寫一個ORM框架,所有的類都只能動態定義,因為只有使用者才能根據表的結構定義出對應的類來。

  

if __name__ == ‘__main__‘:  
    L = MyList()  
    L.add(2)  #增加了add()方法  
    L.add(3)  
    print(L)  

if __name__ == ‘__main__‘:
L = MyList()
L.add(2) #增加了add()方法
L.add(3)
print(L)

[2, 3]

3、精簡ORM框架

#開始編寫ORM框架  
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‘)  
#下一步,就是編寫最復雜的ModelMetaclass了:  
class ModelMetaclass(type):  
    def __new__(cls, name,bases,attrs):  
        if name == ‘Model‘:  #排除掉對Model類的修改;  
            return type.__new__(cls,name,bases,attrs)  
        print(‘Found Model: %s‘%name)  
        mappings = dict()  
        for k,v in attrs.items(): #查找定義的類的所有屬性,  
            if isinstance(v,Field): #如果找到一個Field屬性,  
                print(‘Found mapping: %s ==> %s‘ % (k, v))  
                mappings[k] = v  #就把它保存到一個__mappings__的dict中  
        for k in mappings.keys():  
            attrs.pop(k)  #同時從類屬性中刪除該Field屬性,否則,容易造成運行時錯誤(實例的屬性會遮蓋類的同名屬性);  
        attrs[‘__mappings__‘] = mappings # 保存屬性和列的映射關系  
        attrs[‘__table__‘] = name  # 假設表名和類名一致  把表名保存到__table__中  
        return type.__new__(cls,name,bases,attrs)  
  
#基類Model  
class Model(dict,metaclass=ModelMetaclass):  
    def __init__(self,**kw):  
        super(Model,self).__init__(**kw)  
    def __getattr__(self, item):  #沒有找到的屬性,就在這裏找  
        try:  
            return self[item]  
        except KeyError:  
            raise AttributeError(r"‘Model‘ object has no attrs :‘%s‘"%item)  
  
    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))  
  
#子類User  
# 定義類的屬性到列的映射:  
class User(Model):  
    id = IntegerField(‘id‘)  
    name = StringField(‘username‘)  
    email = StringField(‘email‘)  
    password = StringField(‘password‘)  
    age = IntegerField(‘age‘)  

#開始編寫ORM框架
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‘)
#下一步,就是編寫最復雜的ModelMetaclass了:
class ModelMetaclass(type):
def __new__(cls, name,bases,attrs):
if name == ‘Model‘: #排除掉對Model類的修改;
return type.__new__(cls,name,bases,attrs)
print(‘Found Model: %s‘%name)
mappings = dict()
for k,v in attrs.items(): #查找定義的類的所有屬性,
if isinstance(v,Field): #如果找到一個Field屬性,
print(‘Found mapping: %s ==> %s‘ % (k, v))
mappings[k] = v #就把它保存到一個__mappings__的dict中
for k in mappings.keys():
attrs.pop(k) #同時從類屬性中刪除該Field屬性,否則,容易造成運行時錯誤(實例的屬性會遮蓋類的同名屬性);
attrs[‘__mappings__‘] = mappings # 保存屬性和列的映射關系
attrs[‘__table__‘] = name # 假設表名和類名一致 把表名保存到__table__中
return type.__new__(cls,name,bases,attrs)

#基類Model
class Model(dict,metaclass=ModelMetaclass):
def __init__(self,**kw):
super(Model,self).__init__(**kw)
def __getattr__(self, item): #沒有找到的屬性,就在這裏找
try:
return self[item]
except KeyError:
raise AttributeError(r"‘Model‘ object has no attrs :‘%s‘"%item)

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))

#子類User
# 定義類的屬性到列的映射:
class User(Model):
id = IntegerField(‘id‘)
name = StringField(‘username‘)
email = StringField(‘email‘)
password = StringField(‘password‘)
age = IntegerField(‘age‘)

 

當用戶定義一個class User(Model)時,Python解釋器首先在當前類User的定義中查找metaclass,如果沒有找到,就繼續在父類Model中查找metaclass,找到了,就使用Model中定義的metaclassModelMetaclass來創建User類,也就是說,metaclass可以隱式地繼承到子類,但子類自己卻感覺不到。


ModelMetaclass中,一共做了幾件事情:


  1. 排除掉對Model類的修改;


  2. 在當前類(比如User)中查找定義的類的所有屬性,如果找到一個Field屬性,就把它保存到一個__mappings__的dict中,同時從類屬性中刪除該Field屬性,否則,容易造成運行時錯誤(實例的屬性會遮蓋類的同名屬性);


  3. 把表名保存到__table__中,這裏簡化為表名默認為類名。


Model類中,就可以定義各種操作數據庫的方法,比如save()delete()find()update等等。


我們實現了save()方法,把一個實例保存到數據庫中。因為有表名,屬性到字段的映射和屬性值的集合,就可以構造出INSERT語句。

編寫代碼試試:
if __name__ == ‘__main__‘:  
    u = User(id = 12345,name = ‘john‘,email = ‘[email protected]‘,password = ‘666666‘)  
    u.age = 12  
    u.save()  

if __name__ == ‘__main__‘:
u = User(id = 12345,name = ‘john‘,email = ‘[email protected]‘,password = ‘666666‘)
u.age = 12
u.save()

Found Model: User  
Found mapping: id ==> <IntegerField:id>  
Found mapping: name ==> <StringField:username>  
Found mapping: email ==> <StringField:email>  
Found mapping: password ==> <StringField:password>  
Found mapping: age ==> <IntegerField:age>  
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)  
ARGS: [12345, ‘john‘, ‘[email protected]‘, ‘666666‘, 12]  

Found Model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: age ==> <IntegerField:age>
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)
ARGS: [12345, ‘john‘, ‘[email protected]‘, ‘666666‘, 12]

 

即User類中做好了到列的映射的類屬性,都能添加到sql語句中,而未做好了到列的映射的類屬性:如height,則會過濾掉,實現數據庫列的字段的高度可定制
if __name__ == ‘__main__‘:  
    u = User(id = 12345,name = ‘john‘,email = ‘[email protected]‘,password = ‘666666‘,height = 180)  
    # u.age = 12  
    u.save()  

if __name__ == ‘__main__‘:
u = User(id = 12345,name = ‘john‘,email = ‘[email protected]‘,password = ‘666666‘,height = 180)
# u.age = 12
u.save()

Found Model: User  
Found mapping: id ==> <IntegerField:id>  
Found mapping: name ==> <StringField:username>  
Found mapping: email ==> <StringField:email>  
Found mapping: password ==> <StringField:password>  
Found mapping: age ==> <IntegerField:age>  
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)  
ARGS: [12345, ‘john‘, ‘[email protected]‘, ‘666666‘, None]  


Found Model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: age ==> <IntegerField:age>
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)
ARGS: [12345, ‘john‘, ‘[email protected]‘, ‘666666‘, None]


即使在初始化的時候傳進了height參數,依然會被過濾掉,而定義好了到列的映射的類屬性age,即使沒有賦值,也會被默認為None,並添加到數據庫中。 註:可以看到,save()方法已經打印出了可執行的SQL語句,以及參數列表,只需要真正連接到數據庫,執行該SQL語句,就可以完成真正的功能。下面入庫操作,還有剩下的刪改查操作可自行解決。

python——type()、metaclass元類和精簡ORM框架