Python是一門面向對象的語言,因此Python中數字、字符串、列表、集合、字典、函數、類等都是對象。python
利用 type()
來查看Python中的各對象類型git
In [11]: # 數字
In [12]: type(10)
Out[12]: int
In [13]: type(3.1415926)
Out[13]: float
In [14]: # 字符串
In [15]: type('a')
Out[15]: str
In [16]: type("abc")
Out[16]: str
In [17]: # 列表
In [18]: type(list)
Out[18]: type
In [19]: type([])
Out[19]: list
In [20]: # 集合
In [21]: type(set)
Out[21]: type
In [22]: my_set = {1, 2, 3}
In [23]: type(my_set)
Out[23]: set
In [24]: # 字典
In [25]: type(dict)
Out[25]: type
In [26]: my_dict = {'name': 'hui'}
In [27]: type(my_dict)
Out[27]: dict
In [28]: # 函數
In [29]: def func():
...: pass
...:
In [30]: type(func)
Out[30]: function
In [31]: # 類
In [32]: class Foo(object):
...: pass
...:
In [33]: type(Foo)
Out[33]: type
In [34]: f = Foo()
In [35]: type(f)
Out[35]: __main__.Foo
In [36]: # type
In [37]: type(type)
Out[37]: type
複製代碼
能夠看出程序員
1
是 int類型 的對象abc
是 str類型 的對象list、set、dict
類型func
是 function類型 的對象Foo
建立出來的對象 f
是 Foo
類型,其類自己 Foo
則是 type類型 的對象。type
自己都是type類型的對象類就是擁有相等功能和相同的屬性的對象的集合web
在大多數編程語言中,類就是一組用來描述如何生成一個對象的代碼段。在 Python 中這一點仍然成立:編程
In [1]: class ObjectCreator(object):
...: pass
...:
In [2]: my_object = ObjectCreator()
In [3]: print(my_object)
<__main__.ObjectCreator object at 0x0000021257B5A248>
複製代碼
可是,Python中的類還遠不止如此。類一樣也是一種對象。是的,沒錯,就是對象。只要你 使用關鍵字 class
,Python解釋器在執行的時候就會建立一個對象。markdown
下面的代碼段:app
>>> class ObjectCreator(object):
… pass
…
複製代碼
將在內存中建立一個對象,名字就是 ObjectCreator
。這個 對象(類對象ObjectCreator)擁有建立對象(實例對象)的能力。可是,它的本質仍然是一個對象,因而乎你能夠對它作以下的操做:編程語言
以下示例:svg
In [39]: class ObjectCreator(object):
...: pass
...:
In [40]: print(ObjectCreator)
<class '__main__.ObjectCreator'> In [41]:# 看成參數傳遞
In [41]: def out(obj):
...: print(obj)
...:
In [42]: out(ObjectCreator)
<class '__main__.ObjectCreator'> In [43]: # hasattr 判斷一個類是否有某種屬性
In [44]: hasattr(ObjectCreator, 'name')
Out[44]: False
In [45]: # 新增類屬性
In [46]: ObjectCreator.name = 'hui'
In [47]: hasattr(ObjectCreator, 'name')
Out[47]: True
In [48]: ObjectCreator.name
Out[48]: 'hui'
In [49]: # 將類賦值給變量
In [50]: obj = ObjectCreator
In [51]: obj()
Out[51]: <__main__.ObjectCreator at 0x212596a7248>
In [52]:
複製代碼
由於類也是對象,你能夠在運行時動態的建立它們,就像其餘任何對象同樣。首先,你能夠在函數中建立類,使用 class
關鍵字便可。函數
def cls_factory(cls_name):
""" 建立類工廠 :param: cls_name 建立類的名稱 """
if cls_name == 'Foo':
class Foo():
pass
return Foo # 返回的是類,不是類的實例
elif cls_name == 'Bar':
class Bar():
pass
return Bar
複製代碼
IPython 測驗
MyClass = cls_factory('Foo')
In [60]: MyClass
Out[60]: __main__.cls_factory.<locals>.Foo # 函數返回的是類,不是類的實例
In [61]: MyClass()
Out[61]: <__main__.cls_factory.<locals>.Foo at 0x21258b1a9c8>
複製代碼
但這還不夠動態,由於你仍然須要本身編寫整個類的代碼。因爲類也是對象,因此它們必須是經過什麼東西來生成的纔對。
當你使用class關鍵字時,Python解釋器自動建立這個對象。但就和Python中的大多數事情同樣,Python仍然提供給你手動處理的方法。
type 還有一種徹底不一樣的功能,動態的建立類。
type能夠接受一個類的描述做爲參數,而後返回一個類。(要知道,根據傳入參數的不一樣,同一個函數擁有兩種徹底不一樣的用法是一件很傻的事情,但這在Python中是爲了保持向後兼容性)
type 能夠像這樣工做:
type(類名, 由父類名稱組成的元組(針對繼承的狀況,能夠爲空),包含屬性的字典(名稱和值))
好比下面的代碼:
In [63]: class Test:
...: pass
...:
In [64]: Test()
Out[64]: <__main__.Test at 0x21258b34048>
In [65]:
複製代碼
能夠手動像這樣建立:
In [69]:# 使用type定義類
In [69]: Test2 = type('Test2', (), {})
In [70]: Test2()
Out[70]: <__main__.Test2 at 0x21259665808>
複製代碼
咱們使用 Test2
做爲類名,而且也能夠把它當作一個變量來做爲類的引用。類和變量是不一樣的,這裏沒有任何理由把事情弄的複雜。即 type函數 中第1個實參,也能夠叫作其餘的名字,這個名字表示類的名字
In [71]: UserCls = type('User', (), {})
In [72]: print(UserCls)
<class '__main__.User'> In [73]:
複製代碼
使用 help
來測試這2個類
In [74]: # 用 help 查看 Test類
In [75]: 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)
In [76]: # 用 help 查看 Test2類
In [77]: 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)
In [78]:
複製代碼
type 接受一個字典來爲類定義屬性,所以
Parent = type('Parent', (), {'name': 'hui'})
複製代碼
能夠翻譯爲:
class Parent(object):
name = 'hui'
複製代碼
而且能夠將 Parent
當成一個普通的類同樣使用:
In [79]: Parent = type('Parent', (), {'name': 'hui'})
In [80]: print(Parent)
<class '__main__.Parent'> In [81]: Parent.name
Out[81]: 'hui'
In [82]: p = Parent()
In [83]: p.name
Out[83]: 'hui'
複製代碼
固然,你能夠繼承這個類,代碼以下:
class Child1(Parent):
name = 'jack'
sex = '男'
class Child2(Parent):
name = 'mary'
sex = '女'
複製代碼
就能夠寫成:
Child1 = type('Child1', (Parent, ), {'name': 'jack', 'sex': '男'})
In [85]: Child2 = type('Child2', (Parent, ), {'name': 'mary', 'sex': '女'})
In [87]: Child1.name, Child1.sex
Out[87]: ('jack', '男')
In [88]: Child2.name, Child2.sex
Out[88]: ('mary', '女')
複製代碼
注意:
最終你會但願爲你的類增長方法。只須要定義一個有着恰當簽名的函數並將其做爲屬性賦值就能夠了。
In [89]: Parent = type('Parent', (), {'name': 'hui'})
In [90]: # 定義函數
In [91]: def get_name(self):
...: return self.name
...:
In [92]: Child3 = type('Child3', (Parent, ), {'name': 'blob', 'get_name': get_name})
In [93]: c3 = Child3()
In [94]: c3.name
Out[94]: 'blob'
In [95]: c3.get_name()
Out[95]: 'blob'
複製代碼
In [96]: Parent = type('Parent', (), {'name': 'hui'})
In [97]: # 定義靜態方法
In [98]: @staticmethod
...: def test_static():
...: print('static method called...')
...:
In [100]: Child4 = type('Child4', (Parent, ), {'name': 'zhangsan', 'test_static': test_static})
In [101]: c4 = Child4()
In [102]: c4.test_static()
static method called...
In [103]: Child4.test_static()
static method called...
複製代碼
In [105]: Parent = type('Parent', (), {'name': 'hui'})
In [106]: # 定義類方法
In [107]: @classmethod
...: def test_class(cls):
...: print(cls.name)
...:
In [108]: Child5 = type('Child5', (Parent, ), {'name': 'lisi', 'test_class': test_class})
In [109]: c5 = Child5()
In [110]: c5.test_class()
lisi
In [111]: Child5.test_class()
lisi
複製代碼
你能夠看到,在Python中,類也是對象,你能夠動態的建立類。這就是當你使用關鍵字 class
時 Python
在幕後作的事情,就是經過元類來實現的。
較爲完整的使用 type 建立類的方式:
class Animal(object):
def eat(self):
print('吃東西')
def dog_eat(self):
print('喜歡吃骨頭')
def cat_eat(self):
print('喜歡吃魚')
Dog = type('Dog', (Animal, ), {'tyep': '哺乳類', 'eat': dog_eat})
Cat = type('Cat', (Animal, ), {'tyep': '哺乳類', 'eat': cat_eat})
# ipython 測驗
In [125]: animal = Animal()
In [126]: dog = Dog()
In [127]: cat = Cat()
In [128]: animal.eat()
吃東西
In [129]: dog.eat()
喜歡吃骨頭
In [130]: cat.eat()
喜歡吃魚
複製代碼
元類就是用來建立類的【東西】。你建立類就是爲了建立類的實例對象,不是嗎?可是咱們已經學習到了Python中的類也是對象。
元類就是用來建立這些類(對象)的,元類就是類的類,你能夠這樣理解爲:
MyClass = MetaClass() # 使用元類建立出一個對象,這個對象稱爲「類」
my_object = MyClass() # 使用「類」來建立出實例對象
複製代碼
你已經看到了type可讓你像這樣作:
MyClass = type('MyClass', (), {})
複製代碼
這是由於函數 type
其實是一個元類。type
就是 Python在背後用來建立全部類的元類。如今你想知道那爲何 type 會所有采用小寫形式而不是 Type 呢?好吧,我猜這是爲了和 str 保持一致性,str是用來建立字符串對象的類,而 int 是用來建立整數對象的類。type 就是建立類對象的類。你能夠經過檢查 __class__
屬性來看到這一點。所以 Python中萬物皆對象
如今,對於任何一個 __class__
的 __class__
屬性又是什麼呢?
In [136]: a = 10
In [137]: b = 'acb'
In [138]: li = [1, 2, 3]
In [139]: a.__class__.__class__
Out[139]: type
In [140]: b.__class__.__class__
Out[140]: type
In [141]: li.__class__.__class__
Out[141]: type
In [142]: li.__class__.__class__.__class__
Out[142]: type
複製代碼
所以,元類就是建立類這種對象的東西。type 就是 Python的內建元類,固然了,你也能夠建立本身的元類。
__metaclass__
屬性你能夠在定義一個類的時候爲其添加 __metaclass__
屬性。
class Foo(object):
__metaclass__ = something…
...省略...
複製代碼
若是你這麼作了,Python就會用元類來建立類Foo。當心點,這裏面有些技巧。你首先寫下 class Foo(object)
,可是類Foo尚未在內存中建立。Python會在類的定義中尋找 __metaclass__
屬性,若是找到了,Python就會用它來建立類Foo,若是沒有找到,就會用內建的 type
來建立這個類。
class Foo(Bar):
pass
複製代碼
Python作了以下的操做:
__metaclass__
這個屬性嗎?若是有,Python會經過 __metaclass__
建立一個名字爲Foo的類(對象)__metaclass__
,它會繼續在 Bar(父類) 中尋找 __metaclass__
屬性,並嘗試作和前面一樣的操做。__metaclass__
,它就會在模塊層次中去尋找 __metaclass__
,並嘗試作一樣的操做。__metaclass__
,Python就會用內置的 type
來建立這個類對象。如今的問題就是,你能夠在 __metaclass__
中放置些什麼代碼呢?
答案就是:能夠建立一個類的東西。那麼什麼能夠用來建立一個類呢?type,或者任何使用到type或者子類化的type均可以。
元類的主要目的就是爲了當建立類時可以自動地改變類。
假想一個很傻的例子,你決定在你的模塊裏全部的類的屬性都應該是大寫形式。有好幾種方法能夠辦到,但其中一種就是經過在模塊級別設定 __metaclass__
。採用這種方法,這個模塊中的全部類都會經過這個元類來建立,咱們只須要告訴元類把全部的屬性都改爲大寫形式就萬事大吉了。
幸運的是,__metaclass__
實際上能夠被任意調用,它並不須要是一個正式的類。因此,咱們這裏就先以一個簡單的函數做爲例子開始。
# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):
# class_name 會保存類的名字 Foo
# class_parents 會保存類的父類 object
# 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 # 設置Foo類的元類爲upper_attr
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True
f = Foo()
print(f.BAR)
複製代碼
# -*- 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)
# 再類的繼承()中使用metaclass
class Foo(object, metaclass=upper_attr):
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True
f = Foo()
print(f.BAR)
複製代碼
再作一次,這一次用一個真正的 class
來當作元類。
class UpperAttrMetaClass(type):
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'
複製代碼
__new__ 是在__init__以前被調用的特殊方法
__new__是用來建立對象並返回之的方法
而__init__只是用來將傳入的參數初始化給對象
這裏,建立的對象是類,咱們但願可以自定義它,因此咱們這裏改寫__new__
複製代碼
就是這樣,除此以外,關於元類真的沒有別的可說的了。但就元類自己而言,它們實際上是很簡單的:
如今回到咱們的大主題上來,到底是爲何你會去使用這樣一種容易出錯且晦澀的特性?
好吧,通常來講,你根本就用不上它:
「元類就是深度的魔法,99%的用戶應該根本沒必要爲此操心。若是你想搞清楚到底是否須要用到元類,那麼你就不須要它。那些實際用到元類的人都很是清楚地知道他們須要作什麼,並且根本不須要解釋爲何要用元類。」 —— Python界的領袖 Tim Peters
源代碼已上傳到 Gitee
PythonKnowledge: Python知識寶庫,歡迎你們來訪。
✍ 碼字不易,還望各位大俠多多支持❤️。
新建文件夾X
大天然用數百億年創造出咱們現實世界,而程序員用幾百年創造出一個徹底不一樣的虛擬世界。咱們用鍵盤敲出一磚一瓦,用大腦構建一切。人們把1000視爲權威,咱們反其道行之,捍衛1024的地位。咱們不是鍵盤俠,咱們只是平凡世界中不凡的締造者 。