@java
在django中編寫models的時候遇到了元類的相關操做
而且在mini-web框架編寫的時候也遇到了相關的問題
意識到深刻理解元類很是的重要因此補票重學python
學習且引用來自博客:https://www.cnblogs.com/intimacy/p/8119449.htmlgit
python中一切都是對象,類也是對象
那麼意味着,類能夠執行如下操做github
>>> print(ObjectCreator) # 打印此對象 <class '__main__.ObjectCreator'> >>> def echo(o): ... print(o) ... >>> echo(ObjectCreator) # 將此對象當作方法的參數 <class '__main__.ObjectCreator'> >>> print(hasattr(ObjectCreator, 'new_attribute')) False >>> ObjectCreator.new_attribute = 'foo' # 給此對象添加屬性 >>> print(hasattr(ObjectCreator, 'new_attribute')) True >>> print(ObjectCreator.new_attribute) foo >>> ObjectCreatorMirror = ObjectCreator # 將此對象賦值給一個變量 >>> print(ObjectCreatorMirror.new_attribute) foo >>> print(ObjectCreatorMirror()) <__main__.ObjectCreator object at 0x8997b4c>
由於類是對象,那麼意味着能夠動態的生成
粗略的覺得,在java等語言中,類使用來定義一個對象的,也就是用來定義起亞數據類型的,私覺得不能夠更改。可是python好像就能夠更改類的源代碼,致使你想在運行的時候從新規定一下類中的屬性和方法也是能夠的,好比下面的代碼傳入foo參數的時候在內存中定義一個class對象,可是傳入其餘的時候就不定義web
>>> def choose_class(name): ... if name == 'foo': ... class Foo(object): ... pass ... return Foo # 返回Foo類,而不是對象 ... else: ... class Bar(object): ... pass ... return Bar ... >>> MyClass = choose_class('foo') >>> print(MyClass) # 方法返回類,不返回對象 <class '__main__.Foo'> >>> print(MyClass()) # 能夠經過方法返回的類建立對象 <__main__.Foo object at 0x89c6d4c>
但這彷佛不是動態建立的,由於咱們寫了一段代碼來生成他,而後python解釋器遇到class關鍵字的時候就自動給咱們程序建立了一個類對象,那麼如何本身建立一個,使用type數據庫
>>> print(type(1)) <type 'int'> >>> print(type("1")) <type 'str'> >>> print(type(ObjectCreator)) <type 'type'> >>> print(type(ObjectCreator())) <class '__main__.ObjectCreator'>
type在咱們學習python的歷程中能夠知道是用來判斷數據類型的,可是type確實能夠用來建立一個類對象,格式以下django
type(類對象的名稱, 元組形式的全部父類對象 (繼承用,能夠爲空),包含屬性名和屬性值的字典)編程
>>> class MyShinyClass(object): ... pass ------------------------------------------------------------------- >>> MyShinyClass = type('MyShinyClass', (), {}) # 返回一個類對象 >>> print(MyShinyClass) <class '__main__.MyShinyClass'> >>> print(MyShinyClass()) # 經過返回的類對象建立一個對象 <__main__.MyShinyClass object at 0x8997cec>
上面兩部分代碼實現的功能是同樣的
再如框架
>>> class Foo(object): ... bar = True -------------------------------------------------------------- >>> Foo = type('Foo', (), {'bar':True}) #能夠向正常的類同樣使用 >>> print(Foo) <class '__main__.Foo'> >>> print(Foo.bar) True >>> f = Foo() >>> print(f) <__main__.Foo object at 0x8a9b84c> >>> print(f.bar) True
這兩部分代碼的功能也是同樣的,因此讓咱們再次熟悉一下type建立類對象的使用方法,第一個參數爲類的名字,第二個參數爲多繼承使用的元組,第三個參數爲一個列表,用來定義類中的方法和屬性
>>> def echo_bar(self): ... print(self.bar) ... >>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) >>> hasattr(Foo, 'echo_bar') False >>> hasattr(FooChild, 'echo_bar') True >>> my_foo = FooChild() >>> my_foo.echo_bar() True
定義方法的時候如上,固然能夠再定義了這個類的時候往裏面加其餘的方法
>>> def echo_bar_more(self): ... print('yet another method') ... >>> FooChild.echo_bar_more = echo_bar_more >>> hasattr(FooChild, 'echo_bar_more') True
通過上述能夠知道在python中,類就是對象,你能在運行中動態的建立類
那麼metaclass是啥呢
metaclass是建立class的東西,定義類的目的,就是爲了建立一個對象,可是咱們已經知道了,python中類就是一個對象,那麼metaclass就是建立這種對象(類)的東西,他們是用來描述類的,能夠理解爲
MyClass = MetaClass() MyObject = MyClass() #使用metaclass建立了一個類對象,在使用這個類對象去建立其餘的對象
咱們已經知道,type能夠爲咱們建立一個對象
是由於方法type其實是一個metaclass,type就是python用來建立其餘類的metaclass, 你能夠經過__class__屬性來驗證這一點 也就是說type就是用來建立其餘class的class,在Python中一切皆對象,一切皆對象,包括int str function(方法) class(類),全部一切都是對象,並且他們都是有某一個類建立
>>> 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'>
因此一個metaclass就是用來建立類對象的東西,你能夠稱之爲「類工廠」。而type就是Python內建的metaclass(類工廠),固然你能定義你本身的metaclass
在python中建立一個對象以前,回去找metaclass,若是沒有指定的話就使用type來建立類對象
Python會: 一、在Foo中有沒有指定__metaclass__屬性 二、若是找到,就會經過__metaclass__指定的東西建立Foo類對象(注意此處是類對象) 三、若是找不到,就會去MODULE(模塊)級別去找,並建立Foo類對象(但只適用於沒有繼承任何父類的類,基本上就是老式類) 四、若是都找不到,就會經過Bar(第一個父類)的metaclass(頗有多是type)來建立Foo類對象, 五、這裏要注意__metaclass__不會被繼承,而父類的__class__(Bar.__class__)會被子類繼承.即:若是Bar用了__metaclass__屬性經過type()而不是type.__new__()建立Bar類對象,那麼子類不會繼承這種行爲。 如今問題是:能夠將什麼指定給__metaclass__。 答案是:能建立類的東西。 什麼能建立類呢?type type的子類 和全部使用type的
自定義metaclass
metaclass的主要做用就是建立類時使類發生變化 一般作API時纔會用到,就是建立和上下文相吻合的類(這句翻的太爛)。 舉個例子:你決定全部模塊下的類的屬性都要大寫,有不少種方法實現此需求,其中一種就是在module級別設置__metaclass__屬性。 這樣一來,此模塊下全部類都經過指定的metaclass建立,你只須要告訴metaclass將全部屬性大寫便可。 幸運的是__metaclass__只要可被調用便可,它沒必要是一個類。 因此咱們先舉個例子,用一個方法作metaclass
# the metaclass will automatically get passed the same argument that you usually pass to `type` def upper_attr(future_class_name, future_class_parents, future_class_attr): """ Return a class object, with the list of its attribute turned into uppercase. """ # pick up any attribute that doesn't start with '__' and uppercase it uppercase_attr = {} for name, val in future_class_attr.items(): if not name.startswith('__'): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val # let `type` do the class creation return type(future_class_name, future_class_parents, uppercase_attr) __metaclass__ = upper_attr # this will affect all classes in the module class Foo(): # global __metaclass__ won't work with "object" though # but we can define __metaclass__ here instead to affect only this class # and this will work with "object" children bar = 'bip' print(hasattr(Foo, 'bar')) # Out: False print(hasattr(Foo, 'BAR')) # Out: True f = Foo() print(f.BAR) # Out: 'bip'
如今使用一個類作metaclass來實現
``python # remember that
typeis actually a class like
strand
int`
class UpperAttrMetaclass(type):
# new is the method called before init
# it's the method that creates the object and returns it
# while init just initializes the object passed as parameter
# you rarely use new, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override new
# you can do some stuff in init too if you wish
# some advanced use involves overriding call as well, but we won't
# see this
def new(upperattr_metaclass, 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()] = val else: uppercase_attr[name] = val return type(future_class_name, future_class_parents, uppercase_attr)
```
上面代碼說的new方法是在init方法以前調用的,new方法是建立一個對象並返回他,init方法只是經過傳入參數初始化一個對象,咱們幾乎用不到new方法,若是你不是想去控制類對象如何生成
上面的方式不是很"面向對象",由於咱們直接調用了type方法,並且沒有重寫也沒有調用父類的__new__方法,下面這種方式就好一些
**class UpperAttrMetaclass(type): def __new__(upperattr_metaclass, 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()] = val else: uppercase_attr[name] = val # reuse the type.__new__ method # this is basic OOP, nothing magic in there return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)**
你可能已經注意到了參數"upperattr_metaclass",這個參數並無什麼特殊的,__new__方法老是以所在類中的類對象做爲第一個參數,就像普通方法中的self參數同樣,將實例做爲第一個參數。 固然我用的名字都見名知意,但就像self,全部的參數也有簡短的名字,因此一個真正的metaclass會像下面這樣:
class UpperAttrMetaclass(type): def __new__(cls, clsname, bases, dct): uppercase_attr = {} for name, val in dct.items(): if not name.startswith('__'): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val return type.__new__(cls, clsname, bases, uppercase_attr)
咱們能夠經過使用super關鍵字使類更清晰:
class UpperAttrMetaclass(type): def __new__(cls, clsname, bases, dct): uppercase_attr = {} for name, val in dct.items(): if not name.startswith('__'): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)
實際上,metaclass在作黑魔法時尤爲 有用,也就是作複雜的事情。至於metaclass自己,仍是很簡單的: 一、攔截類的生成 二、修改類 三、返回修改後的類
爲何用metaclass而不用方法呢?
既然__metaclass__能接收一切可被調用的東西,爲何要用class呢,class但是比方法複雜的多啊!!!
主要是如下幾個緣由:
一、目的更明確。你看到UpperAttrMetaclass(type),就知道是什麼意思
二、能用"面向對象編程",metaclass能夠繼承metaclass能夠重寫父類方法,甚至可使用其餘metaclass。
三、某個類若是你指定了metaclass,則它的子類是這個metaclass的實例,但若是指定了metaclass爲function則不是(一個類不能是某個方法的實例)。
四、代碼結構會更好。上面使用metaclass的例子已經很是簡單了,經過使用metaclass的狀況都是比較複雜的。將全部方法組織在一個class中,能使你的代碼更易讀。
五、能夠在__new__,__init__和__call__中作你想作的事,雖然你能都在__new__寫,但有些人仍是習慣在__init_中實現。
六、叫metaclass,該死的,這名字仍是有些意義的。
class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField()
但若是你這樣作
guy = Person(name='bob', age='35') print(guy.age)
代碼不返回IntegerField對象,而返回int,甚至是直接在數據庫中取出的數據。 這多是由於,models.Model定義了__metaclass__,而指向的metaclass將你定義的簡單的Person類變爲數據庫中的一個字段。 利用metaclass,Django暴露一套API使得複雜的事情看起來很簡單,而在幕後作真正的工做。
最後:
首先,類就是能建立實例的對象。實際上,類是metaclass的實例
>>> class Foo(object): pass >>> id(Foo) 142630324
在Python中一切都是對象(實例),他們不是類的實例就是metaclass的實例。 除了type。type的metaclass是本身,實際上在純Python中是不能建立type的,而是經過在Python的實現層做弊實現的。 其次,metaclass是很複雜的。若是你只要對類作一點點調整,就不要使用metaclass,你能夠用另外兩種技術來實現: 猴子補丁(monkey patching) 裝飾器 當你須要對類作調整,99%的狀況下你能夠經過以上兩種方式實現。 可是98%的狀況是:你徹底不須要對類作調整
我的博客網站
我的GitHub地址
我的公衆號: