Python面向對象篇之元類,附Django Model核心原理


關於元類,我寫過一篇,如果你只是了解元類,看下面這一篇就足夠了。

Python面向對象之類的方法和屬性

本篇是深度解剖,如果你覺得元類用不到,呵呵,那是因為你不了解Django。

在Python中有一個type類,所有的類都是基於type類生成的,可謂萬物之母。

如此廣袤無垠的python生態圈,都是由type產生出來的。

Python面向對象總計包含五大部分:

常用部分:

3.class(類,或者叫實例生成器)

4.instance(實例)

5.實例的各種屬性與方法,我們平常使用python時,調用的就是它們。

不常用部分(類從何而來):

1.type

2.metaclass(元類,或者叫類生成器)

 兩種方式創建類:

# 創建一個Hello類,擁有屬性say_hello
class Hello():
    def say_hello(self, name='gregory'):
        print( 'Hello, %s.'% name)
# 從Hello類創建一個實例hello
hello =Hello()
# 使用hello調用方法say_hello
hello.say_hello()

def func(self, name='gregory'):
# 創建函數func
    print('Hi, %s.'% name)
Hi= type('Hi', (object,), dict(say_hello=func))
# 通過type創建Hi class
hello=Hi()
hello.say_hello()

效果一樣的。

Hi= type('Hi', (object,), dict(say_hello=func))

第一個參數: 類名。我是誰。

第二個參數:當前類的基類。我從哪里來,也就是我的“父類”,以上實例中我的父類是“object”——python中一種非常初級的類。

第三個參數:類的成員。我要到哪里去,將需要調用的方法和屬性包含到一個字典里,再作為參數傳入。以上實例中,say_hello方法包裝進了字典中。

type可以直接生成類(class),但也可以先生成元類(metaclass),再使用元類批量定制類(class)。

元類均被命名后綴為Metalass,元類的生命周期:

class SayMetaClass(type):
    #元類是由“type”衍生而出,所以父類需要傳入type。
    def __new__(cls, name, bases, attrs):
        #元類的操作都在 __new__中完成,它的第一個參數是將創建的類,之后的參數即是三大永恆命題:類名,基類,類的成員。

        attrs['say_'+ name] =lambda self, value, saying=name:print(saying + ','+ value +'!')
        #創造屬性和方法,由元類創建的類叫“Hello”,那創建時就自動有了一個叫“say_Hello”的類方法
        # 然后又將類的名字“Hello”作為默認參數saying,傳到了方法里面。
        # 然后把hello方法調用時的傳參作為value傳進去,最終打印出來。

        return type.__new__(cls, name, bases, attrs)
        #傳承類名,父類,屬性

class Hello(object, metaclass =SayMetaClass):
    # 創建類,通過元類創建的類,第一個參數是父類,第二個參數是metaclass
    pass

hello =Hello()# 創建實列
hello.say_Hello( 'gregory')# 調用實例方法

class Nihao(object,metaclass=SayMetaClass):
    pass

nihao=Nihao()
nihao.say_Nihao("greg 李")

應用:Django的核心思想是“Object Relational Mapping”,即對象-關系映射,簡稱ORM。

這是Django的一大難點,但學完了元類,一切變得清晰。你對Django的理解將更上一層樓!

通過元類創建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')


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 k, v in attrs.items():
            if isinstance(v, Field):
                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, **kwarg):
        super(Model, self).__init__(**kwarg)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError("'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    # 模擬建表操作
    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        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='Gregory', email='292409083@qq.com', password='iamgreg')
u.save()

這是Django中的Model版塊核心原理!

運行結果:

Found model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
SQL: insert into User(id,username,email,password) values (12345,Gregory,292409083@qq.com,iamgreg)
ARGS: [12345, 'Gregory', '292409083@qq.com', 'iamgreg']

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM