关于python创建类的深入理解
背景
我們知道在python中一切皆為對象的概念,那我們們來看一段程序
class Foo(object):def __init__(self, name):self.name = namef = Foo("sty")
print(type(f))
print(type(Foo))#output:
# <class '__main__.Foo'>
# <class 'type'>
如果按照一切事物都是對象的理論:f對象是通過執(zhí)行Foo類的構(gòu)造方法創(chuàng)建,那么Foo類對象應(yīng)該也是通過執(zhí)行某個類的 構(gòu)造方法 創(chuàng)建.
通過打印我們可以知道:f對象是Foo類的一個實例,Foo類對象是 type 類的一個實例,即:Foo類對象 是通過type類的構(gòu)造方法創(chuàng)建。
創(chuàng)建類的深入
那么我們可以知道將可以有兩種方法來在python中創(chuàng)建類了
普通方法
class Foo(object):def talk(self):print 'hello sty'f = Foo()
f.talk()
特殊方式
def func(self):print('hello sty')Foo = type('Foo', (object,), {'talk': func}) #創(chuàng)建Foo類f = Foo()
f.talk()
#type第一個參數(shù):類名
#type第二個參數(shù):當(dāng)前類的基類
#type第三個參數(shù):類的成員
上面的第一種方法是我們常用而且熟悉的,而第二種方法也和第一種方法有著同樣的效果。
我們還可以在第二種方法的基礎(chǔ)上加上構(gòu)造函數(shù):
def func(self):print('hello %s' % self.name)def __init__(self, name, age):self.name = nameself.age = ageFoo = type('Foo', (object,), {'talk': func, '__init__': __init__})f = Foo("jack", 22)
f.talk()
#output:
#hello jack
所以我們可以得到一個結(jié)論:類默認(rèn)是由 type 類實例化產(chǎn)生的
你可以看到, 在Python中, 類也是對象, 你可以動態(tài)的創(chuàng)建類。 這就是當(dāng)你使用關(guān)鍵字class時Python在幕后做的事情, 而這就是通過元類來實現(xiàn)的。
什么是元類?
元類就是創(chuàng)建類的東西.
你是為了創(chuàng)建對象才定義類的,對吧?
但是我們已經(jīng)知道了Python的類是對象.
這里,元類創(chuàng)建類.它們是類的類,你可以把它們想象成這樣:
MyClass = MetaClass()
MyObject = MyClass()
我們可以通過type來實現(xiàn)這樣的效果:
MyClass = type('MyClass', (), {})
這是因為 type 就是一個元類. type 是Python中創(chuàng)建所有類的元類
現(xiàn)在你可能納悶為啥子 type 用小寫而不寫成 Type ?
我想是因為要跟 str 保持一致, str 創(chuàng)建字符串對象, int 創(chuàng)建整數(shù)對象. type 正好創(chuàng)建類對象.
你可以通過檢查 class 屬性來看到這一點.
Python中所有的東西都是對象.包括整數(shù),字符串,函數(shù)還有類.所有這些都是對象.所有這些也都是從類中創(chuàng)建
的:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
那么,class的class屬性是什么?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
所以,元類就是創(chuàng)建類對象的東西.
如果你愿意你也可以把它叫做’類工廠’. type 是Python的內(nèi)建元類,當(dāng)然,你也可以創(chuàng)建你自己的元類
那么問題來了
類默認(rèn)是由 type 類實例化產(chǎn)生,type類中如何實現(xiàn)的創(chuàng)建類?類又是如何創(chuàng)建對象?
答:類中有一個屬性 metaclass,其用來表示該類由 誰 來實例化創(chuàng)建,所以,我們可以為 metaclass 設(shè)置一個type類的派生類,從而查看 類 創(chuàng)建的過程。
metaclass屬性
當(dāng)你創(chuàng)建一個函數(shù)的時候,你可以添加 metaclass 屬性:
class Foo(object):__metaclass__ = something...
如果這么做了,python就會用元類來創(chuàng)建類Foo
你首先寫下 class Foo(object), 但是類對象 Foo 還沒有在內(nèi)存中創(chuàng)建.
Python將會在類定義中尋找 metaclass .如果找到了就用它來創(chuàng)建類對象 Foo .如果沒找到,就會默認(rèn)
用 type 創(chuàng)建類.
把下面這段話反復(fù)讀幾次。
當(dāng)你寫下如下代碼時候:
class Foo(Bar):pass
Python將會這樣運行:
在 Foo 中有沒有 _metaclass_ 屬性?
如果有,Python會在內(nèi)存中通過 metaclass 創(chuàng)建一個名字為 Foo 的類對象(我說的是類對象,跟緊我的思
路).
如果Python沒有找到 metaclass , 它會繼續(xù)在Bar(父類) 中尋找 metaclass屬性 , 并嘗試做和前面
同樣的操作.
如果Python在任何父類中都找不到 metaclass , 它就會在模塊層次中去尋找 metaclass , 并嘗試做
同樣的操作。
如果還是找不到 metaclass ,Python就會用內(nèi)置的 type 來創(chuàng)建這個類對象。
現(xiàn)在的問題就是, 你可以在 metaclass 中放置些什么代碼呢?
答案就是:可以創(chuàng)建一個類的東西。
那么什么可以用來創(chuàng)建一個類呢? type , 或者任何使用到 type 或者子類化 type 的東東都可以。
自定義元類
元類的主要目的就是為了當(dāng)創(chuàng)建類時能夠自動地改變類.
通常, 你會為API做這樣的事情, 你希望可以創(chuàng)建符合當(dāng)前上下文的類.
假想一個很傻的例子, 你決定在你的模塊里所有的類的屬性都應(yīng)該是大寫形式。 有好幾種方法可以辦到,
但其中一種就是通過在模塊級別設(shè)定 metaclass .
采用這種方法, 這個模塊中的所有類都會通過這個元類來創(chuàng)建, 我們只需要告訴元類把所有的屬性都改成
大寫形式就萬事大吉了。
def upper_attr(future_class_name, future_class_parents, future_class_attr):uppercase_attr = {}for name, val in future_class_attr.items():if not name.startswith('__'):uppercase_attr[name.upper()] = valelse:uppercase_attr[name] = valreturn type(future_class_name, future_class_parents, uppercase_attr)# __metaclass__ = upper_attr# class Foo():
# bar1 = 'bip'
class Foo(object, metaclass=upper_attr):bar='bip'# False
print(hasattr(Foo, 'bar'))# True
print(hasattr(Foo, 'BAR'))f = Foo()
# bip
print(f.BAR)
繼續(xù)深入
先來看一段代碼,我們來了解一下__new__
class Foo(object):def __init__(self,name):self.name = nameprint("Foo __init__")def __new__(cls, *args, **kwargs):print("Foo __new__",cls, *args, **kwargs)return object.__new__(cls) #繼承父親的__new__方法,如果注釋改行,類沒法實例化f = Foo("Alex")
print("f",f)
print("fname",f.name)# output
# Foo __new__ <class '__main__.Foo'> sty
# Foo __init__
# f <__main__.Foo object at 0x000001EF9AB7B518>
# fname sty
我們可以知道__new__是在__init__之前執(zhí)行的,并且我們在把
return object.__new__(cls)
注釋掉之后,類是沒辦法實例化的,所以我們可以知道,類其實是通過__new__來實例化的,并且是在__new__中調(diào)用的__init__,所以在實例化的時候是先執(zhí)行的__new__,然后再執(zhí)行的__init__,一般情況下是不用寫的,父類是有的。
再看段代碼,了解下__call__的執(zhí)行順序:
class MyType(type):def __init__(self,*args,**kwargs):print("Mytype __init__",*args,**kwargs)def __call__(self, *args, **kwargs):print("Mytype __call__", *args, **kwargs)obj = self.__new__(self)print("obj ",obj,*args, **kwargs)print(self)self.__init__(obj,*args, **kwargs)return objdef __new__(cls, *args, **kwargs):print("Mytype __new__",*args,**kwargs)return type.__new__(cls, *args, **kwargs)print('here...')
class Foo(object, metaclass = MyType):def __init__(self,name):self.name = nameprint("Foo __init__")def __new__(cls, *args, **kwargs):print("Foo __new__",cls, *args, **kwargs)return object.__new__(cls) #如果注釋改行,類沒法實例化f = Foo("sty")
print("f",f)
print("fname",f.name)#output:
#here...
# Mytype __new__ Foo (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x0000014DBE6A4950>, '__new__': <function Foo.__new__ at 0x0000014DBE6A49D8>}
# Mytype __init__ Foo (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x0000014DBE6A4950>, '__new__': <function Foo.__new__ at 0x0000014DBE6A49D8>}
# Mytype __call__ sty
# Foo __new__ <class '__main__.Foo'>
# obj <__main__.Foo object at 0x0000014DBE6BB5C0> sty
# <class '__main__.Foo'>
# Foo __init__
# f <__main__.Foo object at 0x0000014DBE6BB5C0>
# fname sty
通過執(zhí)行結(jié)果我們可以知道:
類的生成 調(diào)用 順序依次是 __new__ --> __init__ --> __call__
結(jié)語
這有什么卵用?
大多數(shù)情況下是沒什么卵用的,
“元類就是深度的魔法, 99%的用戶應(yīng)該根本不必為此操心。 如果你想搞清楚究竟是否需要用到元類, 那么你就不需要它。
那些實際用到元類的人都非常清楚地知道他們需要做什么, 而且根本不需要解釋 為什么要用元類。 ” —— Python界的領(lǐng)袖 Tim Peters
元類的主要用途是創(chuàng)建API。 一個典型的例子是Django ORM。
如果想更加深入的了解:
可以查看:
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python#
參考文獻(xiàn)
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python#
http://www.cnblogs.com/alex3714/articles/5213184.html
轉(zhuǎn)載請注明出處:
CSDN:樓上小宇_home:http://blog.csdn.net/sty945
簡書:樓上小宇:http://www.jianshu.com/u/1621b29625df
總結(jié)
以上是生活随笔為你收集整理的关于python创建类的深入理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3+ 解决写入中文乱码的问题
- 下一篇: python 网络编程之Socket通信