python筆記6:元類與ORM

Table of Contents

元類

01 全局對象與內嵌模塊

02 元類

03 type使用

04 元類應用補充

元類實現ORM

01什麼是orm

02實現ORM

03完善對數據類型的檢測

04 python操作系統知識回顧

05 複習


元類

01 全局對象與內嵌模塊

什麼是類?類是一個對象(類對象-創建實例對象的模子對象),用它創建的對象爲實例對象

class ObjectCreater(object):

Py中萬物皆對象,Linux萬物皆文件

Python中有內嵌函數(可以直接調用的函數),如:

input("請輸入>>")

print("XXX")

list()

tuple()

dict()

set()

Python運行時默認加載了內建/內嵌模塊,其中帶有這些常用函數

查看到底有哪些函數可以直接用,globals返回可調用的一個字典:

globals()

新定義一個全局變量:

a=100

再查看globals,會增加一行

'a':100,

新定義一個全局函數:

def AA():

       pass

再查看globals,最新一行會增加

'AA':<function __main__.AA>

新定義一個類:

class BB():

       pass

globals

'BB':__main__.BB

定義類時,會給這個類創建內存空間,globals中的名字指向剛剛創建的對象空間

 

萬物皆對象,這個變量也是對象

在字典中不能出現兩個相同的key,所以最後導入的、最後賦引用的會覆蓋,類、函數、變量不能重名

同理,類的名字指向了這個類對象

類與普通對象的區別只是,它擁有創建其他實例對象空間的能力

但是這個globals中沒有print函數,在內嵌模塊builtin中。查看這個模塊:

xx=globals()#xx指向了這個字典

xx['__builtin__'].__dict__

'input':<function input>

'print':<function print>

調用其中函數

yy=xx['__builtin__'].__dict__#保存這個字典

yy['print']("hah")

輸出:hah

 

ipython比單用python時有更多命令可以直接用

把所有輸出過的東西存儲在Out字典中,把所有輸入過的東西存儲在In字典中

ipython有一個sqlite數據庫,內嵌型(它不需要建立服務器,讀取時打開文件)

02 元類

需求-改變類空間內容,如:添加一個屬性

類:創建實例對象,實例對象有自己的內存空間,同時也有一部分共享類對象中的數據

元類(python造物主):可以創建類

類也是對象,你可以在運行時創建它們,就像其他任何對象一樣。

可以在函數中創建類,使用class關鍵字即可

>>> def choose_class(name):

       if name == 'foo':

           class Foo(object):

               pass

           return Foo     # 返回的是類,不是類的實例

       else:

           class Bar(object):

               pass

           return Bar

 

>>> MyClass = choose_class('foo')

>>> print(MyClass)  # 函數返回的是類,不是類的實例

<class '__main__'.Foo>

>>> print(MyClass())  # 可以通過這個類創建類實例,也就是對象

<__main__.Foo object at 0x89c6d4c>

比較標準的創建類的方式是type(一般type用來返回一個對象的類型type(100)

type(類名, 由父類名稱組成的元組(針對無繼承的情況,可以爲空),包含屬性的字典(名稱和值))

B = type("B", (), {"num":100,"num2":200})

查看類:

print(help(B))

輸出:

num=100

num2=200

Type是一個元類,Type的返回值是一個類對象

字典不一樣,則屬性不一樣

03 type使用

如果元類創建類時需要繼承

B = type("B", (), {"num":100,"num2":200})

Test22=type("Test22",(B,),{})#元組中至少要放一個','

查看新類

print(help(Test22))

class Test22(B)

...

num = 100

num2 = 200

有繼承的類、屬性

添加方法:添加屬性時指向方法引用

def test_2(self):

       print(">>實例方法")

Test3=type("Test3",(),{"test_2":test_2})

創建實例對象

t3=Test3()

t3.test_2()

輸出:

>>實例方法

添加類方法

#類方法

@classmethod

def test_3(cls):

       print(">>類方法")

Test4=type("Test4",(),{"test_2":test_2,"test_3":test_3})

添加靜態方法

#靜態方法

@staticmethod

def test_4():

       print(">>靜態方法")

Test5=type("Test5",(),{"test_2":test_2,"test_3":test_3,"test_4":test_4})

Demo

class T(object):

       pass

t=T()

t.__class__#查看是哪個類創建的

輸出

__main__.T

t.__class__.__class__

輸出

type

即使直接寫,類還是type創建的

元類創建類對象,類對象創建實例對象

元類的功能:

#-*- coding:utf-8 -*-

def upper_attr(class_name, class_parents, class_attr):

 

    # 遍歷屬性字典,把不是__開頭的屬性名字變爲大寫

    new_attr = {}

    for name,value in class_attr.items():

        if not name.startswith("__"):#過濾掉隱藏的默認屬性

            new_attr[name.upper()] = value

 

    # 調用type來創建一個類

    return type(class_name, class_parents, new_attr)

 

class Foo(object, metaclass=upper_attr):

    bar = 'bip'

 

print(hasattr(Foo, 'bar'))

print(hasattr(Foo, 'BAR'))

 

f = Foo()

print(f.BAR)

在定義普通類Foo時,如果形如:

class Foo(object):

    bar = 'bip'

就調用默認的Type創建類對象,等價於:

Foo=type("Foo",(object,),{"bar":'bip'})

Python3:添加參數metaclass=upper_attr:不管在類中定義什麼屬性名,通通變成大寫

Python2:

class Foo(object):

    __metaclass__ = upper_attr  # 設置Foo類的元類爲upper_attr

不直接調用默認type創建,調用自定義函數創建

自定義函數的返回值:一個類對象

# 調用type來創建一個類

    return type(class_name, class_parents, new_attr)

函數被調用後能像type一樣獲取類名、父類、類屬性,就可以在創建時對他們進行修改後再給type

04 元類應用補充

一般情況不會使用元類

裝飾器在不修改原來函數條件下,調用函數前後加新功能

元類不修改原來類定義中內容,但可以修改類屬性等

之前一直使用的函數當作元類使用,修改爲類:

class UpperAttrMetaClass(type):

    # __new__ 是在__init__之前被調用的特殊方法

    # __new__是用來創建對象並返回的方法

    # 而__init__只是用來將傳入的參數初始化給對象

    # 你很少用到__new__,除非希望能夠控制對象的創建

    # 這裏,創建的對象是類,我們希望能夠自定義它,所以改寫__new__

    # 還有一些高級的用法會涉及到改寫__call__特殊方法,但是這裏不用

    def __new__(cls, class_name, class_parents, class_attr):

        # 遍歷屬性字典,把不是__開頭的屬性名字變爲大寫

        new_attr = {}

        for name, value in class_attr.items():

            if not name.startswith("__"):

                new_attr[name.upper()] = value

 

        return type(class_name, class_parents, new_attr)

在定義了多個類,想要統一修改某些功能屬性時,可以考慮元類

__new__也涉及到單例

Python中沒有數組

元類實現ORM

01什麼是orm

Django的核心思想:Object Relation Mapping對象與關係映射

創建實例對象,用類名當表名,用類屬性當字段,操作實例對象,對應mysql語句

原生Sql方式:

insert into User(id,name) values(123, "laowang")

ORM方式:

u=User(id=123, name="laowang")#創建實例對象

u.save()

02實現ORM

Python多繼承時,super按照繼承元組的順序調用,保證被多類繼承的那個類只執行一次

#繼承type類,是元類

class ModelMetaClass(type):#創建元類對象

       #重寫new方法,傳入參數name=User,parents=(,),

       #attrs={"uid":(...),"name":(...)}

    def __new__(cls, class_name, class_parents, class_attrs):

 

        mapping = dict()

        #遍歷屬性元組

        for name, value in class_attrs.items():

               #判斷類型是否是指定的Str或Int

            if isinstance(value, tuple):

                print("Found mapping %s --> %s" % (name, value))

                mapping[name] = value#保存在mapping

        #循環mapping中已經保存的字段名

        for name in mapping.keys():

               # 在屬性字典中刪除

            class_attrs.pop(name)

        #將mapping字典,表名保存在類屬性中

        class_attrs["__mapping__"] = mapping

        class_attrs["__table__"] = class_name

        #創建新的類對象

        # 其中屬性包括mapping,table表名,沒有4個類屬性

        return type.__new__(cls, class_name, class_parents, class_attrs)

 

class User(metaclass=ModelMetaClass):#用自定義元類創建user類對象

    uid = ('uid', "int unsigned")

    name = ('username', "varchar(30)")

    email = ('email', "varchar(30)")

    password = ('password', "varchar(30)")

    #創建實例對象後,這些屬性沒了,變成了mapping字典和table字符串

    # 這樣不需要用戶自己創建字典,比較簡單

    def __init__(self, **kwargs):#創建實例對象時需要傳參,以字典的方式保存所有參數

           #拆參數字典

        for name, value in kwargs.items():

               #getattr可獲取方法中屬性 setattr往實例對象添加屬性

               #由於屬性名不確定,難以直接用self.XXX=XXX賦值

            setattr(self, name, value)

 

    def save(self):

        fields = []

        args = []

        #self中保存了各個屬性

        #mapping字典中保存着表結構"uid屬性名":('uid字段名',"unsigned")

        for k, v in self.__mapping__.items():

               #字段名添加在列表中

            fields.append(v[0])

            #拿着key找到實例屬性的值,添加到args

            args.append(getattr(self, k, None))

        #插入數據庫時需要表名

        # ",".join給字符串鏈接所有字段名、值,以“,”分隔

        sql = "insert into %s(%s) values(%s)" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))

        print("SQL: %s" % sql)

 

#通過類創建實例對象,會調用__new__,__init__

u = User(uid=123, name="zfx", email="[email protected]", password="6666")

u.save()

定義類屬性,操作的時候也操作相同名字的類屬性,但對應的在表中保存時,用原來類定義中屬性保存下的字段名,不過此時已經保存在mapping中了

03完善對數據類型的檢測

當前版本只是連接屬性值,當類型爲字符串時,沒有在sql爲它加雙引號

sql = "insert into %s(%s) values(%s)" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))

改爲:

# 整型不用雙引號,字符串需要

        args_temp=list()

        for temp in args:

               if isinstance(temp,int):#數字直接添加

                      args_temp.append(str(temp))

               elif isinstance(temp,str):

                      args_temp.append("""'%s'"""%temp)

使用join時,會剝去外層的引號

此時其他的類也需要定義__init__和save方法,將他們抽象到父類Model中:

class Model(object,metaclass=ModelMetaClass):

    def __init__(self, **kwargs):

        for name, value in kwargs.items():

            setattr(self, name, value)

 

    def save(self):

        fields = []

        args = []

        for k, v in self.__mapping__.items():

            fields.append(v[0])

            args.append(getattr(self, k, None))

        args_temp=list()

        for temp in args:

               if isinstance(temp,int):

                      args_temp.append(str(temp))

               elif isinstance(temp,str):

                      args_temp.append("""'%s'"""%temp)

        sql = "insert into %s(%s) values(%s)" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))

        print("SQL: %s" % sql)

子類只需繼承父類:

class User(Model):

    uid = ('uid', "int unsigned")

    name = ('username', "varchar(30)")

    email = ('email', "varchar(30)")

    password = ('password', "varchar(30)")

真正的django中,字段不是簡單的元組,而是對象

04 python操作系統知識回顧

進程之間是獨立的(並行,不共享資源),進程間通信:管道(無名管道、命名管道)、消息隊列、內存映射、socket。

這些想起以前操作系統的課上學過,作業還用C語言自己實現過

網絡:udp、tcp網絡進程間通信,不同進程通過端口區分

Tcp的長連接和短連接:長連接-長時間在線服務、可重新利用套接字,短連接-查一下就斷開

線程間(多任務)共享資源,進程、線程、協程的區別:

進程(不共享、更穩定)-分配的單位,真正的多任務

線程(共享、更快速)-調度的單位、python默認用GIL,

協程-在線程中有阻塞,消耗時間,此時可調用其他函數,假的多任務

互斥鎖-應對資源競爭,可能造成死鎖

GIL全局解釋器鎖-邏輯上同一時刻只有一個線程在執行,但其實在多個切換

計算密集型-進程

網絡密集型-攜程

正則表達式比較常用,有機會要深化學習

05 複習

http過程主要:

解析域名對應的ip,三次握手連接ip服務器,連接成功發送tcp數據、http協議,返回瀏覽器顯示

其中:

請求交給網關,網關將ip地址變、mac不變放到萬維網,

深拷貝、淺拷貝:傳一個引用、一個新的地方的引用

私有化:__class__

Import導入模塊:import導入可以讓變量指向模塊中的變量,from再import則不行

面向對象:封裝繼承多態

多繼承與MRO順序

With與上下文管理器

Mysql視圖:虛擬表,數據庫可能變化,但代碼已經寫好

事務A C I D原子性、一致性、隔離性、持久性

索引:使用B-tree記錄誰在哪

WSGI協議服務器與web框架間規定

閉包裝飾器

前面的python提高部分,正則部分,多任務(線程、進程、協程)都很值得再看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章