Python中metaclass解釋__Python

來源:互聯網
上載者:User

Classes as objects

首先,在認識metaclass之前,你需要認識下python中的class。python中class的奇怪特性借鑒了smalltalk語言。大多數語言中,classes僅僅是用於描述怎樣建立一個對象的代碼端。在某種程度上說,python中的class也是這樣的。

  >>> class ObjectCreator(object):  ...       pass  ...   >>> my_object = ObjectCreator()  >>> print my_object  <__main__.ObjectCreator object at 0x8974f2c>
但是,python中的classes同時還是objects,是的,看的沒錯,是objects,一旦你使用關鍵字class,python將執行並且產生一個對象(object),命令

  >>> class ObjectCreator(object):  ...       pass  ... 
將在記憶體中建立一個名字為ObjectCreator的對象。

這個對象(類)自己具有建立對象(執行個體)的能力,這也是為什麼被稱之為類

因為它是一個對象,所以它應該具有對象的一些特性: 你可以把它assign給一個變數 你可以copy它 你可以給它添加屬性 你可以把它作為一個函數的參數

例如:

  >>> print ObjectCreator # you can print a class because it's an object  <class '__main__.ObjectCreator'>  >>> def echo(o):  ...       print o  ...   >>> echo(ObjectCreator) # you can pass a class as a parameter  <class '__main__.ObjectCreator'>  >>> print hasattr(ObjectCreator, 'new_attribute')  False  >>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class  >>> print hasattr(ObjectCreator, 'new_attribute')  True  >>> print ObjectCreator.new_attribute  foo  >>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable  >>> print ObjectCreatorMirror.new_attribute  foo  >>> print ObjectCreatorMirror()  <__main__.ObjectCreator object at 0x8997b4c>

Creating classes dynamically 因為classes是對象,所以你可以想對象一樣動態建立他們。

首先,你可以在一個函數中使用關鍵字建立它:

  >>> def choose_class(name):  ...     if name == 'foo':  ...         class Foo(object):  ...             pass  ...         return Foo # return the class, not an instance  ...     else:  ...         class Bar(object):  ...             pass  ...         return Bar  ...       >>> MyClass = choose_class('foo')   >>> print MyClass # the function returns a class, not an instance  <class '__main__.Foo'>  >>> print MyClass() # you can create an object from this class  <__main__.Foo object at 0x89c6d4c>
但是,這個不是很動態,因為你還是要自己寫全整個函數。因為類是對象,所以它們可以被產生。當你使用關鍵字class的時候,python自動的建立這些對象,所以就像python中大部分事情一樣,我們同樣能夠手動的產生。

還記得函數type嗎。就是那個能告訴你物件類型的函數:

>>> print type(1)<type 'int'>>>> print type("1")<type 'str'>>>> print type(ObjectCreator)<type 'type'>>>> print type(ObjectCreator())<class '__main__.ObjectCreator'>
令人驚訝的是type同樣具有一種完全不同的能力,就是我們需要的動態建立函數。type可以將類的描述作為參數,然後產生一個類。type是這樣工作的:

  type(name of the class,        tuple of the parent class (for inheritance, can be empty),        dictionary containing attributes names and values)
例如:

>>> class MyShinyClass(object):...       pass
可以利用以下方式手動建立:

  >>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object  >>> print MyShinyClass  <class '__main__.MyShinyClass'>  >>> print MyShinyClass() # create an instance with the class  <__main__.MyShinyClass object at 0x8997cec>
可以看出,我們以MyShinyClass作為class的name, 函數很明顯了,只不過是些不同的參數變化,沒有理由去複雜化,用原文作者的話說:They can be different,but there is no reason to complicate things。

What are metaclasses (finally) 終於要開是解釋metaclasses了,我也等了好久,Metaclasses are the 'stuff' that creates classes. 這個是文章的作者說的,就是說它是個可以建立類的東東,你定義類是為了建立對象是嗎。但是在python中我們視類為對象,so,metaclasses就是來建立對象的。它們是類的類,你可以這樣想象它們:

  MyClass = MetaClass()  MyObject = MyClass()
你可以看出type讓你做了同樣的事情:

  MyClass = type('MyClass', (), {})
這是因為函數type事實上是一個metaclass,在python中,type是一個metaclass用於在後台建立類。現在你知道為什麼它為什麼用的是小寫,而不是大寫的Type。
well, 你是不是想到了str/int等函數呢,str用於建立字串對象,int建立整型對象,那type只不過是建立類對象的類而已。你可以通過查看__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的建立者是誰呢。

  >>> age.__class__.__class__  <type 'type'>  >>> name.__class__.__class__  <type 'type'>  >>> foo.__class__.__class__  <type 'type'>  >>> b.__class__.__class__  <type 'type'>
這下應該稍微明白了些吧,當然,我們也可以建立我們自己的metaclass。

The __metaclass__ attribute 你可以在建立類的時候添加__metaclass__屬性:

class Foo(object):  __metaclass__ = something...  [...]
python將使用metaclass去建立類Foo,要小心哦,你寫了class Foo(object),但是Foo還沒有在記憶體中建立,python會在你的類中尋找__metaclass__如果找到了就用它建立,如果沒有找到,它將使用type去建立類。

詳細點說吧,當你定義類時:

class Foo(object):   pass
python將首先查看在類定義中有沒有__metaclass__屬性,沒有將在parent中找,沒有去module層級找,如果還沒有找到,最後的殺招type。

好,最後的問題是,我們該在__metaclass__中放些什麼東東呢。

Custom metaclasses 一個metaclass的主要目的是在類建立的時候自動的去改變它,讀起來有點拗。一般來說,當你寫些APIs,而且這些APIs要滿足當前的內容相關的時候,可以考慮使用metaclass。想象一下,當你的module中的類需要將它們的屬性寫為小寫時候,我們就可以試試在moudle層級metaclass,這時,module中的所有類都將由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 moduleclass 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: Falseprint hasattr(Foo, 'BAR')# Out: Truef = Foo()print f.BAR# Out: 'bip'
現在,我們用一個類來實現metaclass:

# remember that `type` is actually a class like `str` and `int`# so you can inherit from itclass 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)
但是這個不是真正的OOP。我們可以直接調用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,這個沒有什麼特別:一個方法常常將當前的執行個體作為首個參數,就像平常的方法中的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去做些需要introspection,操作繼承/變數比如__dict__等。事實上,metaclass也確實能做些複雜的事。但是,以下是不複雜的:

理解一個類的建立 改變類 返回一個修改的類

Why would you use metaclasses classes instead of functions? 還是堅持寫完吧。因為__metaclass__能接受任意的調用,那為什麼要選用看起來比較複雜的類呢。有一下幾個原因:

興建類的目的比較清晰,參考上面的UpperAttrMetaclass(type) 你可以使用OOP。Metaclass能繼承自metaclass,覆蓋父類方法 你可以更好的組織你的代碼結構。make code easy to read 你可以使用__new__, __init__, __call__ 都將做了metaclass,總要作點事吧。(這個比較牽強(:-)

Why the hell would you use metaclasses? python guru寫的解釋道行太淺看不懂,不翻譯了,不過他說了個例子,就是Django中的ORM模型使用了,例如我們可以定義資料模型如下:

  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__,而且將你定義的簡單的Person轉化為複雜的連結到資料庫欄位。

Django通過expose 一個簡單的API,將許多複雜的事情讓我們看起來很簡單,而且通過metaclass,在後台利用API重建code。

The last word 最後,作者提到了type的exception,還有class alterations:意思就是說我們大部分時間是用不到class alteration的,用到的時候呢,可以使用monkey patching和class decorators來實現,所以留給metaclass的空間很小了。

全劇終。。。

文章的詳情清查看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

這個也寫的蠻不錯的:http://xiaocong.github.io/blog/2012/06/12/python-metaclass/

偶爾翻看stackoverflow的python文章,受益匪淺,今天看到metaclass講的太好了,就試著翻譯出來試試,歡迎大家互相交流。




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.