Table of Contents
元類
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提高部分,正則部分,多任務(線程、進程、協程)都很值得再看