主頁 > 知識庫 > Python進(jìn)階學(xué)習(xí)之帶你探尋Python類的鼻祖-元類

Python進(jìn)階學(xué)習(xí)之帶你探尋Python類的鼻祖-元類

熱門標(biāo)簽:唐山智能外呼系統(tǒng)一般多少錢 廣告地圖標(biāo)注app 激戰(zhàn)2地圖標(biāo)注 白銀外呼系統(tǒng) 騰訊外呼線路 哈爾濱ai外呼系統(tǒng)定制 陜西金融外呼系統(tǒng) 海南400電話如何申請 公司電話機(jī)器人

Python是一門面向?qū)ο蟮恼Z言,所以Python中數(shù)字、字符串、列表、集合、字典、函數(shù)、類等都是對象。

利用 type() 來查看Python中的各對象類型

In [11]: # 數(shù)字

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]: # 函數(shù)

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

可以看出

數(shù)字 1int類型 的對象

字符串 abcstr類型 的對象

列表、集合、字典是 type類型 的對象,其創(chuàng)建出來的對象才分別屬于 list、set、dict 類型

函數(shù) funcfunction類型 的對象

自定義類 Foo 創(chuàng)建出來的對象 fFoo 類型,其類本身 Foo 則是 type類型 的對象。

type 本身都是type類型的對象

一、類也是對象

類就是擁有相等功能和相同的屬性的對象的集合

在大多數(shù)編程語言中,類就是一組用來描述如何生成一個對象的代碼段。在 Python 中這一點(diǎn)仍然成立:

In [1]: class ObjectCreator(object):
   ...:     pass
   ...:

In [2]: my_object = ObjectCreator()

In [3]: print(my_object)
__main__.ObjectCreator object at 0x0000021257B5A248>

但是,Python中的類還遠(yuǎn)不止如此。類同樣也是一種對象。是的,沒錯,就是對象。只要你 使用關(guān)鍵字 class,Python解釋器在執(zhí)行的時(shí)候就會創(chuàng)建一個對象。

下面的代碼段:

>>> class ObjectCreator(object):
…       pass
…

將在內(nèi)存中創(chuàng)建一個對象,名字就是 ObjectCreator。這個 對象(類對象ObjectCreator)擁有創(chuàng)建對象(實(shí)例對象)的能力。但是,它的本質(zhì)仍然是一個對象,于是乎你可以對它做如下的操作:

1.你可以將它賦值給一個變量

2.你可以拷貝它

3.你可以為它增加屬性

4.你可以將它作為函數(shù)參數(shù)進(jìn)行傳遞

如下示例:

In [39]: class ObjectCreator(object):
    ...:     pass
    ...:

In [40]: print(ObjectCreator)
class '__main__.ObjectCreator'>

In [41]:# 當(dāng)作參數(shù)傳遞

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]:

二、動態(tài)地創(chuàng)建類

因?yàn)轭愐彩菍ο?,你可以在運(yùn)行時(shí)動態(tài)的創(chuàng)建它們,就像其他任何對象一樣。首先,你可以在函數(shù)中創(chuàng)建類,使用 class 關(guān)鍵字即可。

def cls_factory(cls_name):
    """
    創(chuàng)建類工廠
    :param: cls_name 創(chuàng)建類的名稱
    """
    if cls_name == 'Foo':
        class Foo():
            pass
        return Foo  # 返回的是類,不是類的實(shí)例

    elif cls_name == 'Bar':
        class Bar():
            pass
        return Bar

IPython 測驗(yàn)

MyClass = cls_factory('Foo')

In [60]: MyClass
Out[60]: __main__.cls_factory.locals>.Foo # 函數(shù)返回的是類,不是類的實(shí)例

In [61]: MyClass()
Out[61]: __main__.cls_factory.locals>.Foo at 0x21258b1a9c8>

但這還不夠動態(tài),因?yàn)槟闳匀恍枰约壕帉懻麄€類的代碼。由于類也是對象,所以它們必須是通過什么東西來生成的才對。

當(dāng)你使用class關(guān)鍵字時(shí),Python解釋器自動創(chuàng)建這個對象。但就和Python中的大多數(shù)事情一樣,Python仍然提供給你手動處理的方法。

三、使用 type 創(chuàng)建類

type 還有一種完全不同的功能,動態(tài)的創(chuàng)建類。

type可以接受一個類的描述作為參數(shù),然后返回一個類。(要知道,根據(jù)傳入?yún)?shù)的不同,同一個函數(shù)擁有兩種完全不同的用法是一件很傻的事情,但這在Python中是為了保持向后兼容性)

type 可以像這樣工作:

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

比如下面的代碼:

In [63]: class Test:
    ...:     pass
    ...:

In [64]: Test()
Out[64]: __main__.Test at 0x21258b34048>

In [65]:

可以手動像這樣創(chuàng)建:

In [69]:# 使用type定義類

In [69]: Test2 = type('Test2', (), {})

In [70]: Test2()
Out[70]: __main__.Test2 at 0x21259665808>

我們使用 Test2 作為類名,并且也可以把它當(dāng)做一個變量來作為類的引用。類和變量是不同的,這里沒有任何理由把事情弄的復(fù)雜。即 type函數(shù) 中第1個實(shí)參,也可以叫做其他的名字,這個名字表示類的名字

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創(chuàng)建帶有屬性的類

type 接受一個字典來為類定義屬性,因此

Parent = type('Parent', (), {'name': 'hui'})

可以翻譯為:

class Parent(object):
	name = 'hui'

并且可以將 Parent 當(dāng)成一個普通的類一樣使用:

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'

當(dāng)然,你可以繼承這個類,代碼如下:

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', '女')

注意:

  • type 的第2個參數(shù),元組中是父類的名字,而不是字符串
  • 添加的屬性是 類屬性,并不是實(shí)例屬性

五、使用type創(chuàng)建帶有方法的類

最終你會希望為你的類增加方法。只需要定義一個有著恰當(dāng)簽名的函數(shù)并將其作為屬性賦值就可以了。

添加實(shí)例方法

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', '女')

添加靜態(tài)方法

In [96]: Parent = type('Parent', (), {'name': 'hui'})

In [97]: # 定義靜態(tài)方法
    
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中,類也是對象,你可以動態(tài)的創(chuàng)建類。這就是當(dāng)你使用關(guān)鍵字 class 時(shí) Python 在幕后做的事情,就是通過元類來實(shí)現(xiàn)的

較為完整的使用 type 創(chuàng)建類的方式:

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 測驗(yàn)
In [125]: animal = Animal()

In [126]: dog = Dog()

In [127]: cat = Cat()

In [128]: animal.eat()
吃東西

In [129]: dog.eat()
喜歡吃骨頭

In [130]: cat.eat()
喜歡吃魚

六、到底什么是元類(終于到主題了)

元類就是用來創(chuàng)建類的【東西】。你創(chuàng)建類就是為了創(chuàng)建類的實(shí)例對象,不是嗎?但是我們已經(jīng)學(xué)習(xí)到了Python中的類也是對象。

元類就是用來創(chuàng)建這些類(對象)的,元類就是類的類,你可以這樣理解為:

MyClass = MetaClass() # 使用元類創(chuàng)建出一個對象,這個對象稱為“類”
my_object = MyClass() # 使用“類”來創(chuàng)建出實(shí)例對象

你已經(jīng)看到了type可以讓你像這樣做:

MyClass = type('MyClass', (), {})

這是因?yàn)楹瘮?shù) type 實(shí)際上是一個元類。type 就是 Python在背后用來創(chuàng)建所有類的元類?,F(xiàn)在你想知道那為什么 type 會全部采用小寫形式而不是 Type 呢?好吧,我猜這是為了和 str 保持一致性,str是用來創(chuàng)建字符串對象的類,而 int 是用來創(chuàng)建整數(shù)對象的類。type 就是創(chuàng)建類對象的類。你可以通過檢查 __class__ 屬性來看到這一點(diǎn)。因此 Python中萬物皆對象

現(xiàn)在,對于任何一個 __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

因此,元類就是創(chuàng)建類這種對象的東西。type 就是 Python的內(nèi)建元類,當(dāng)然了,你也可以創(chuàng)建自己的元類。

七、metaclass屬性

你可以在定義一個類的時(shí)候?yàn)槠涮砑?__metaclass__ 屬性。

class Foo(object):
    __metaclass__ = something…
    ...省略...

如果你這么做了,Python就會用元類來創(chuàng)建類Foo。小心點(diǎn),這里面有些技巧。你首先寫下 class Foo(object),但是類Foo還沒有在內(nèi)存中創(chuàng)建。Python會在類的定義中尋找 __metaclass__ 屬性,如果找到了,Python就會用它來創(chuàng)建類Foo,如果沒有找到,就會用內(nèi)建的 type 來創(chuàng)建這個類。

class Foo(Bar):
    pass

Python做了如下的操作:

1.Foo中有 __metaclass__ 這個屬性嗎?如果有,Python會通過 __metaclass__ 創(chuàng)建一個名字為Foo的類(對象)

2.如果Python沒有找到 __metaclass__,它會繼續(xù)在 Bar(父類) 中尋找 __metaclass__ 屬性,并嘗試做和前面同樣的操作。

3.如果Python在任何父類中都找不到 __metaclass__,它就會在模塊層次中去尋找 __metaclass__,并嘗試做同樣的操作。

4.如果還是找不到 __metaclass__ ,Python就會用內(nèi)置的 type 來創(chuàng)建這個類對象。

現(xiàn)在的問題就是,你可以在 __metaclass__ 中放置些什么代碼呢?

答案就是:可以創(chuàng)建一個類的東西。那么什么可以用來創(chuàng)建一個類呢?type,或者任何使用到type或者子類化的type都可以。

八、自定義元類

元類的主要目的就是為了當(dāng)創(chuàng)建類時(shí)能夠自動地改變類。

假想一個很傻的例子,你決定在你的模塊里所有的類的屬性都應(yīng)該是大寫形式。有好幾種方法可以辦到,但其中一種就是通過在模塊級別設(shè)定 __metaclass__。采用這種方法,這個模塊中的所有類都會通過這個元類來創(chuàng)建,我們只需要告訴元類把所有的屬性都改成大寫形式就萬事大吉了。

幸運(yùn)的是,__metaclass__ 實(shí)際上可以被任意調(diào)用,它并不需要是一個正式的類。所以,我們這里就先以一個簡單的函數(shù)作為例子開始。

python2中

# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    # class_name 會保存類的名字 Foo
    # class_parents 會保存類的父類 object
    # class_attr 會以字典的方式保存所有的類屬性

    # 遍歷屬性字典,把不是__開頭的屬性名字變?yōu)榇髮?
    new_attr = {}
    for name, value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    # 調(diào)用type來創(chuàng)建一個類
    return type(class_name, class_parents, new_attr)

class Foo(object):
    __metaclass__ = upper_attr # 設(shè)置Foo類的元類為upper_attr
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True

f = Foo()
print(f.BAR)

python3中

# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    #遍歷屬性字典,把不是__開頭的屬性名字變?yōu)榇髮?
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    #調(diào)用type來創(chuàng)建一個類
    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 來當(dāng)做元類。

class UpperAttrMetaClass(type):
    
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍歷屬性字典,把不是__開頭的屬性名字變?yōu)榇髮?
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通過'type'來做類對象的創(chuàng)建
        return type(class_name, class_parents, new_attr)

        # 方法2:復(fù)用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__之前被調(diào)用的特殊方法
__new__是用來創(chuàng)建對象并返回之的方法
而__init__只是用來將傳入的參數(shù)初始化給對象
這里,創(chuàng)建的對象是類,我們希望能夠自定義它,所以我們這里改寫__new__

就是這樣,除此之外,關(guān)于元類真的沒有別的可說的了。但就元類本身而言,它們其實(shí)是很簡單的:

1.攔截類的創(chuàng)建

2.修改類

3.返回修改之后的類

總結(jié)

現(xiàn)在回到我們的大主題上來,究竟是為什么你會去使用這樣一種容易出錯且晦澀的特性?

好吧,一般來說,你根本就用不上它:

“元類就是深度的魔法,99%的用戶應(yīng)該根本不必為此操心。如果你想搞清楚究竟是否需要用到元類,那么你就不需要它。那些實(shí)際用到元類的人都非常清楚地知道他們需要做什么,而且根本不需要解釋為什么要用元類?!?—— Python界的領(lǐng)袖 Tim Peters

到此這篇關(guān)于Python進(jìn)階學(xué)習(xí)之帶你探尋Python類的鼻祖-元類的文章就介紹到這了,更多相關(guān)Python元類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Python中的Nonetype類型怎么判斷
  • python數(shù)據(jù)類型相關(guān)知識擴(kuò)展
  • 一篇文章帶你搞懂Python類的相關(guān)知識
  • 用 Python 元類的特性實(shí)現(xiàn) ORM 框架
  • 詳細(xì)總結(jié)Python類的多繼承知識
  • python 使用Tensorflow訓(xùn)練BP神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)鳶尾花分類
  • Python-typing: 類型標(biāo)注與支持 Any類型詳解
  • python中必會的四大高級數(shù)據(jù)類型(字符,元組,列表,字典)
  • Python如何把不同類型數(shù)據(jù)的json序列化
  • Python基礎(chǔ)之元類詳解
  • Python入門學(xué)習(xí)之類的相關(guān)知識總結(jié)

標(biāo)簽:四川 惠州 黑龍江 黔西 常德 鷹潭 益陽 上海

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Python進(jìn)階學(xué)習(xí)之帶你探尋Python類的鼻祖-元類》,本文關(guān)鍵詞  Python,進(jìn)階,學(xué),習(xí)之,帶你,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Python進(jìn)階學(xué)習(xí)之帶你探尋Python類的鼻祖-元類》相關(guān)的同類信息!
  • 本頁收集關(guān)于Python進(jìn)階學(xué)習(xí)之帶你探尋Python類的鼻祖-元類的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章