一、元類
1、了解類也是對象
Python中的類同樣也是一種對象,隻要你使用關鍵字class,Python解釋器在執行的時候就會建立一個對象。這個對象(類對象)擁有建立對象(執行個體對象)的能力。但是,它的本質仍然是一個對象
2、動态的建立類
因為類也是對象,你可以在運作時動态的建立它們,就像其他任何對象一樣。首先,你可以在函數中建立類,使用class關鍵字即可。代碼如下:
def create_class(name):
if name == "AAA":
class AAA(object):
print("建立AAA類")
return AAA
elif name == "BBB":
class BBB(object):
print("建立BBB類")
return BBB
# 調用create_class方法,并傳遞參數
# 想要建立AAA對象,需要傳遞對應的參數
AAA_class = create_class("AAA")
print(AAA_class)
# 可以通過AAA_class這個類建立示例對象
print(AAA_class())
"""
列印結果:
建立AAA類
<class '__main__.create_class.<locals>.AAA'>
<__main__.create_class.<locals>.AAA object at 0x000002235BBBA860>
"""
但這還不夠動态,因為你仍然需要自己編寫整個類的代碼。由于類也是對象,是以它們必須是通過什麼東西來生成的才對。當你使用class關鍵字時,Python解釋器自動建立這個對象。但就和Python中的大多數事情一樣,Python仍然提供給你手動處理的方法。此時需要用到Python的内置函數:type
type不僅僅可以傳回一個對象的類型,同時,還可以用來建立一個類,即type就是元類(元類可以建立元類,元類也可以建立其他所有的類)
3、使用type建立類
type還有一種完全不同的功能,動态的建立類。
type可以接受一個類的描述作為參數,然後傳回一個類。(要知道,根據傳入參數的不同,同一個函數擁有兩種完全不同的用法是一件很傻的事情,但這在Python中是為了保持向後相容性)
type建立類的文法:
type(類名, 由父類名稱組成的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
# 使用class關鍵詞定義一個類
class Test:
pass
# 以上使用關鍵詞定義一個類的方式,可以是用type如下定義
# 類名叫做Test2,父類為空,屬性為空
Test2 = type("Test2", (), {})
使用help測試兩個類,結果如下:
help(Test)
"""
Help on class Test in module __main__:
class Test(builtins.object)
| # 使用class關鍵詞定義一個類
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
"""
help(Test2)
"""
Help on class Test2 in module __main__:
class Test2(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
"""
4、使用type建立帶有屬性的類
# 使用type建立一個帶有屬性的類
# 類名叫做Test,繼承自object,有一個屬性為bar=True
Test = type("Test", (object, ), {"bar" : True})
"""
以上可以翻譯為:
class Test(object):
bar = True
"""
# 建立一個子類,繼承Test
Child_Class = type("Child_Class", (Test,), {})
help(Test)
"""
Help on class Test in module __main__:
class Test(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| bar = True
"""
help(Child_Class)
"""
class Child_Class(Test)
| Method resolution order:
| Child_Class
| Test
| builtins.object
|
| Data descriptors inherited from Test:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
|
| ----------------------------------------------------------------------
| Data and other attributes inherited from Test:
|
| bar = True
"""
注意:type的第2個參數,元組中是父類的名字,而不是字元串;添加的屬性是類屬性,并不是執行個體屬性
5、使用type建立帶有方法的類
(1)、添加執行個體方法
# 使用type建立一個帶有屬性的類
# 類名叫做Test,繼承自object,有一個屬性為bar=True
Test = type("Test", (object, ), {"bar" : True})
"""
以上可以翻譯為:
class Test(object):
bar = True
"""
# 建立一個子類,繼承Test
Child_Class = type("Child_Class", (Test,), {})
# 定義一個普通的函數
def func1(self):
print("這是一個方法:", self.bar)
# 使用type建立一個類,讓func1指向上面定義的方法
Test1 = type("Test1", (Child_Class, ), {"func1": func1})
# 使用hasattr方法判斷Test1中是否有func1這個屬性
print(hasattr(Test1, "func1")) # True
help(Test1)
"""
Help on class Test1 in module __main__:
class Test1(Child_Class)
| Method resolution order:
| Test1
| Child_Class
| Test
| builtins.object
|
| Methods defined here:
|
| func1(self)
| # 定義一個普通的函數
|
| ----------------------------------------------------------------------
| Data descriptors inherited from Test:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
|
| ----------------------------------------------------------------------
| Data and other attributes inherited from Test:
|
| bar = True
"""
# 使用hasattr方法判斷Child_Class中是否有func1這個屬性
print(hasattr(Child_Class, "func1")) # False
(2)、添加靜态方法
# 使用type建立一個帶有屬性的類
# 類名叫做Test,繼承自object,有一個屬性為bar=True
Test = type("Test", (object, ), {"bar" : True})
"""
以上可以翻譯為:
class Test(object):
bar = True
"""
# 建立一個子類,繼承Test
Child_Class = type("Child_Class", (Test,), {})
# 定義一個普通的函數
def func1(self):
print("這是一個方法:", self.bar)
# 定義一個靜态方法
@staticmethod
def test_static():
print("這是一個靜态方法")
Test1 = type("Test1", (Child_Class, ), {"func1": func1, "test_static": test_static})
test1 = Test1()
test1.test_static() # 這是一個靜态方法
test1.func1() # 這是一個方法: True
(3)、添加類方法
# 使用type建立一個帶有屬性的類
# 類名叫做Test,繼承自object,有一個屬性為bar=True
Test = type("Test", (object, ), {"bar" : True})
"""
以上可以翻譯為:
class Test(object):
bar = True
"""
# 建立一個子類,繼承Test
Child_Class = type("Child_Class", (Test,), {})
# 定義一個普通的函數
def func1(self):
print("這是一個方法:", self.bar)
# 定義一個靜态方法
@staticmethod
def test_static():
print("這是一個靜态方法")
# 定義一個類方法
@classmethod
def test_classmethod(cls):
print(cls.bar)
Test1 = type("Test1", (Child_Class, ), {"func1": func1, "test_static": test_static, "test_classmethod": test_classmethod})
test1 = Test1()
test1.test_classmethod() # True
較為完整的使用type建立類的方式:
class A(object):
num = 100
def print_b(self):
print(self.num)
@staticmethod
def print_static():
print("----haha-----")
@classmethod
def print_class(cls):
print(cls.num)
B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class})
b = B()
b.print_b()
b.print_static()
b.print_class()
""" 結果
100
----haha-----
100 """
6、什麼是元類
元類就是用來建立這些類(對象)的,元類就是類的類;函數type實際上是一個元類,type就是Python在背後用來建立所有類的元類;Python中所有的東西(對象),包括整數、字元串、函數以及類,它們全部都是對象,而且它們都是從一個類建立而來,這個類就是type。
7、__methodclass__屬性(python2中的用法)
在定義一個類的時候為其添加__metaclass__屬性。如下:
class Foo(object):
__metaclass__ = 一個類的引用
...
Python就會用元類來建立類Foo。小心點,這裡面有些技巧。你首先寫下class Foo(object),但是類Foo還沒有在記憶體中建立。Python會在類的定義中尋找__metaclass__屬性,如果找到了,Python就會用它來建立類Foo,如果沒有找到,就會用内建的type來建立這個類。
當寫了一個普通的建立類的方法時,代碼如下:
class Foo():
pass
Python做了如下的操作:
Foo中有__metaclass__這個屬性嗎?如果是,Python會通過__metaclass__建立一個名字為Foo的類(對象)
如果Python沒有找到__metaclass__,它會繼續在Bar(父類)中尋找__metaclass__屬性,并嘗試做和前面同樣的操作。
如果Python在任何父類中都找不到__metaclass__,它就會在子產品層次中去尋找__metaclass__,并嘗試做同樣的操作。
如果還是找不到__metaclass__,Python就會用内置的type來建立這個類對象。
8、自定義元類
自定義元類的主要目的就是為了當建立類時能夠主動的改變類。
使用函數來當作元類:
# 自定義一個将所有屬性都變成大寫字母的方法
# class_name : 類名
# class_parents : 父類元組
# class_attr : 屬性字典
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)
# 如果定義類的屬性中有metaclass這個屬性,則會自動使用metaclass對應的方法去建立目前的類
class Foo(object, metaclass=upper_attr):
bar = 'bip'
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
使用類當作元類:
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被調用的特殊方法
# __new__是用來建立對象并傳回之的方法
# 而__init__隻是用來将傳入的參數初始化給對象
# 你很少用到__new__,除非你希望能夠控制對象的建立
# 這裡,建立的對象是類,我們希望能夠自定義它,是以我們這裡改寫__new__
# 如果你希望的話,你也可以在__init__中做些事情
# 還有一些進階的用法會涉及到改寫__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
# 方法1:通過'type'來做類對象的建立
return type(class_name, class_parents, new_attr)
# 方法2:複用type.__new__方法
# 這就是基本的OOP程式設計,沒什麼魔法
# return type.__new__(cls, class_name, class_parents, new_attr)
# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
bar = 'bip'
# python2的用法
# class Foo(object):
# __metaclass__ = UpperAttrMetaClass
# bar = 'bip'
print(hasattr(Foo, 'bar'))
# 輸出: False
print(hasattr(Foo, 'BAR'))
# 輸出:True
f = Foo()
print(f.BAR)
# 輸出:'bip'
9、元類總結
就元類本身而言,它們的作用很簡單,如下:攔截類的建立、修改類、傳回修改之後的類
"元類就是深度的魔法,99%的使用者應該根本不必為此操心。如果你想搞清楚究竟是否需要用到元類,那麼你就不需要它。那些實際用到元類的人都非常清楚地知道他們需要做什麼,而且根本不需要解釋為什麼要用元類。" —— Python界的領袖 Tim Peters
二、ORM
1、ORM是什麼
ORM 是 python程式設計語言後端web架構 Django的核心思想,“Object Relational Mapping”,即對象-關系映射,簡稱ORM。
一個句話了解就是:建立一個執行個體對象,用建立它的類名當做資料表名,用建立它的類屬性對應資料表的字段,當對這個執行個體對象操作時,能夠對應MySQL語句。
2、通過元類實作簡單的ORM中的insert功能
# 建立一個元類,一個類繼承了type,就是元類
class ModelMetaClass(type):
# cls:類對象 name:類名 bases:父類元組 attrs:屬性字典
def __new__(cls, name, bases, attrs):
mapping = dict()
# 判斷是否需要儲存
for k, v in attrs.items():
# 判斷是否是指定的StringField或者IntegerField的執行個體對象
# 即:判斷v是否是一個元組
if isinstance(v, tuple):
print("Found mapping : %s ==> %s" %(k, v))
# 将k 和 v 放到mapping中
mapping[k] = v
# 根據mapping中的key删除attrs中的對應的屬性字典
for k in mapping.keys():
attrs.pop(k)
# 将之前的屬性和字段對應關系{"uid":('uid','int unsigned'),...}放入屬性字典中
attrs["__mappings__"] = mapping # 儲存屬性和列的映射關系
attrs["__table__"] = name # 讓表名和類名一緻
# 使用type建立類
return type.__new__(cls, name, bases, attrs)
# 實體類
class User(metaclass = ModelMetaClass):
uid = ("uid", "int unsigned")
name = ("username", "varchar(20)")
password = ("password", "varchar(30)")
email = ("email", "varchar(40)")
"""
當實體類使用ModelMetaClass這個元類建立執行個體對象之後,以上的屬性就相當于變為如下屬性:
__mappings__ = {
"uid":("uid", "int unsigned"),
"name":("username", "varchar(20)"),
"password":("password", "varchar(30)"),
"email":("email", "varchar(40)")
}
__table__ = "User"
"""
def __init__(self, **kwargs):
# 建立執行個體對象的時候,kwargs 為: uid = xxx , name = xxx , password = xxx , email = xxx
for name, value in kwargs.items():
# 将kwagrs中沒個對應的參數名和value放入執行個體對象的屬性和對應的值中
setattr(self, name, value)
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0]) # 建立字段名稱清單
args.append(getattr(self, k, None)) # 根據k(uid)擷取執行個體對象中屬性的值,并放入參數清單中
# 編寫sql, ",".join(清單):該方法将清單中的沒個元素取出,并用,号隔開,并傳回字元串
# sql = """insert into %s (%s) values (%s)""" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))
# 以上寫sql的方法會出現資料類型錯誤的問題,即生成的sql如下:
# insert into User (uid,username,password,email) values (1,小名同學,admin123,[email protected])
# 無法在mysql中直接運作
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(args_temp))
print(sql)
# 實踐以上代碼
u = User(uid=1, name='小名同學', password='admin123', email='[email protected]')
u.save()
"""
列印結果如下:
Found mapping : uid ==> ('uid', 'int unsigned')
Found mapping : name ==> ('username', 'varchar(20)')
Found mapping : password ==> ('password', 'varchar(30)')
Found mapping : email ==> ('email', 'varchar(40)')
insert into User (uid,username,password,email) values (1,'小名同學','admin123','[email protected]')
"""
3、将類中除了定義屬性的其他方法抽取到父類中
# 建立一個元類,一個類繼承了type,就是元類
class ModelMetaClass(type):
# cls:類對象 name:類名 bases:父類元組 attrs:屬性字典
def __new__(cls, name, bases, attrs):
mapping = dict()
# 判斷是否需要儲存
for k, v in attrs.items():
# 判斷是否是指定的StringField或者IntegerField的執行個體對象
# 即:判斷v是否是一個元組
if isinstance(v, tuple):
print("Found mapping : %s ==> %s" %(k, v))
# 将k 和 v 放到mapping中
mapping[k] = v
# 根據mapping中的key删除attrs中的對應的屬性字典
for k in mapping.keys():
attrs.pop(k)
# 将之前的屬性和字段對應關系{"uid":('uid','int unsigned'),...}放入屬性字典中
attrs["__mappings__"] = mapping # 儲存屬性和列的映射關系
attrs["__table__"] = name # 讓表名和類名一緻
# 使用type建立類
return type.__new__(cls, name, bases, attrs)
# 定義基類
class Model(object, metaclass=ModelMetaClass):
def __init__(self, **kwargs):
# 建立執行個體對象的時候,kwargs 為: uid = xxx , name = xxx , password = xxx , email = xxx
for name, value in kwargs.items():
# 将kwagrs中沒個對應的參數名和value放入執行個體對象的屬性和對應的值中
setattr(self, name, value)
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0]) # 建立字段名稱清單
args.append(getattr(self, k, None)) # 根據k(uid)擷取執行個體對象中屬性的值,并放入參數清單中
# 編寫sql, ",".join(清單):該方法将清單中的沒個元素取出,并用,号隔開,并傳回字元串
# sql = """insert into %s (%s) values (%s)""" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))
# 以上寫sql的方法會出現資料類型錯誤的問題,即生成的sql如下:
# insert into User (uid,username,password,email) values (1,小名同學,admin123,[email protected])
# 無法在mysql中直接運作
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(args_temp))
print(sql)
# 實體類
class User(Model):
uid = ("uid", "int unsigned")
name = ("username", "varchar(20)")
password = ("password", "varchar(30)")
email = ("email", "varchar(40)")
"""
當實體類使用ModelMetaClass這個元類建立執行個體對象之後,以上的屬性就相當于變為如下屬性:
__mappings__ = {
"uid":("uid", "int unsigned"),
"name":("username", "varchar(20)"),
"password":("password", "varchar(30)"),
"email":("email", "varchar(40)")
}
__table__ = "User"
"""
class Cate(Model):
cid = ("cid", "varchar(10)")
cname = ("cname", "varchar(30")
# 實踐以上代碼
u = User(uid=1, name='小名同學', password='admin123', email='[email protected]')
u.save()
c = Cate(cid='001', cname="商品")
c.save()
"""
列印結果如下:
insert into User (uid,username,password,email) values (1,'小名同學','admin123','[email protected]')
insert into Cate (cid,cname) values ('001','商品')
"""