成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[譯]什么是元類metaclass?

zsirfs / 1215人閱讀

摘要:如果還是沒有找到,就會(huì)使用父類中的元類來(lái)創(chuàng)建類。元類通常用于處理比較復(fù)雜的情況。這是因?yàn)槭褂昧嗽悾鼤?huì)將中定義的字段轉(zhuǎn)換成數(shù)據(jù)庫(kù)中的字段。中所有數(shù)據(jù)類型都是對(duì)象,它們要么是類的實(shí)例要么是元類的實(shí)例。

原文地址:what is metaclass in Python?
我的簡(jiǎn)書地址::nummy

類即對(duì)象

在理解元類之前,需要先掌握Python中的類,Python中類的概念與SmallTalk中類的概念相似。
在大多數(shù)語(yǔ)言中,類是用來(lái)描述如何創(chuàng)建對(duì)象的代碼段,這在Python中也是成立的:

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

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Python中,類其實(shí)也是對(duì)象。當(dāng)我們使用關(guān)鍵字class的時(shí)候,Python會(huì)執(zhí)行這段代碼,然后生成一個(gè)對(duì)象。下面的代碼在內(nèi)存中創(chuàng)建一個(gè)對(duì)象ObjectCreator:

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

當(dāng)一個(gè)對(duì)象具有創(chuàng)建對(duì)象的能力時(shí),就稱該對(duì)象為類。

所以類本質(zhì)上還是一個(gè)對(duì)象,因此它具有以下屬性:

可以將它賦值給其它變量

可以對(duì)它進(jìn)行復(fù)制

可以給它添加屬性

可以將它傳遞給函數(shù)作為參數(shù)

例如:

>>> print(ObjectCreator) # you can print a class because it"s an object

>>> def echo(o):
...       print(o)
... 
>>> echo(ObjectCreator) # you can pass a class as a parameter

>>> 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>
動(dòng)態(tài)創(chuàng)建類

既然類就是對(duì)象,那我們就可以像創(chuàng)建其他對(duì)象一樣動(dòng)態(tài)創(chuàng)建類。
首先,在函數(shù)中使用class創(chuàng)建一個(gè)類:

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

>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

但是上面的例子也稱不上是完全動(dòng)態(tài)的創(chuàng)建類,因?yàn)槲覀冞€需要在其中編寫整個(gè)類的代碼。
既然類就是對(duì)象,那么它們肯定是通過(guò)某個(gè)東西來(lái)創(chuàng)建的。當(dāng)使用class關(guān)鍵字的時(shí)候,Python會(huì)自動(dòng)創(chuàng)建類,Python也提供了方法讓我們手動(dòng)來(lái)創(chuàng)建類。

還記得type()函數(shù)嗎?這個(gè)函數(shù)可以獲取對(duì)象的類型。

>>> print(type(1))

>>> print(type("1"))

>>> print(type(ObjectCreator))

>>> print(type(ObjectCreator()))

type還有另外一個(gè)功能,那就是創(chuàng)建類。type使用類的相關(guān)描述作為參數(shù),然后返回一個(gè)類。
type創(chuàng)建類的語(yǔ)法如下:

type(類名,基類元組(可以為空,用于繼承), 包含屬性或函數(shù)的字典)

例如:

>>> class MyShinyClass(object):
...       pass

上面的類可以使用下面的方法手動(dòng)創(chuàng)建:

>>> MyShinyClass = type("MyShinyClass", (), {}) # returns a class object
>>> print(MyShinyClass)

>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

type也接收一個(gè)字典參數(shù)來(lái)定義類中的屬性:

>>> class Foo(object):
...       bar = True

等價(jià)于

>>> Foo = type("Foo", (), {"bar":True})

通過(guò)type創(chuàng)建的類使用方式跟普通類一樣:

>>> print(Foo)

>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

當(dāng)然也可以繼承:

>>>   class FooChild(Foo):
...         pass

等價(jià)于:

>>> FooChild = type("FooChild", (Foo,), {})
>>> print(FooChild)

>>> print(FooChild.bar) # bar is inherited from Foo
True

最后,我們可能還想給類添加方法,可以先定義一個(gè)函數(shù),然后將它以屬性的方式賦予給類。

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

而且,我們還可以在動(dòng)態(tài)創(chuàng)建類之后,給類添加更多的方法和屬性:

>>> def echo_bar_more(self):
...       print("yet another method")
... 
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, "echo_bar_more")
True
什么是元類?

通常,我們定義類來(lái)創(chuàng)建對(duì)象,但是現(xiàn)在我們知道類也是對(duì)象。那么是通過(guò)什么來(lái)創(chuàng)建類呢?答案就是元類。你可以想象關(guān)系如下:

MyClass = MetaClass()
MyObject = MyClass()

你已經(jīng)知道使用type可以創(chuàng)建類:

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

那是因?yàn)?strong>type函數(shù)實(shí)際上就是一個(gè)元類,Python使用type作為元類來(lái)創(chuàng)建所有的類。
通過(guò)檢查class屬性,我們可以知道,其實(shí)Python中任何數(shù)據(jù)類型都是對(duì)象,包括整型、字符串、函數(shù)以及類,它們都是對(duì)象。它們都是從類中創(chuàng)建的。

>>> age = 35
>>> age.__class__

>>> name = "bob"
>>> name.__class__

>>> def foo(): pass
>>> foo.__class__

>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__

那么__class____class__是什么呢?

>>> age.__class__.__class__

>>> name.__class__.__class__

>>> foo.__class__.__class__

>>> b.__class__.__class__

所以類其實(shí)就是通過(guò)元類來(lái)創(chuàng)建的,你可以將元類稱之為類工廠。
type是內(nèi)置的元類,Python默認(rèn)使用它來(lái)創(chuàng)建類。當(dāng)然,我們也可以定義屬于我們自己的元類。

metaclass屬性

當(dāng)我們創(chuàng)建類的時(shí)候,可以給它添加metaclass屬性:

class Foo(object):
  __metaclass__ = something...
  [...]

如果我們定義了metaclass屬性,Python就會(huì)使用這個(gè)元類來(lái)創(chuàng)建類Foo。
注意,編譯器首先讀取class Foo(object),這時(shí)并不會(huì)在內(nèi)存中創(chuàng)建Foo類。Python會(huì)繼續(xù)查找類定義中的__meatclass__,如果找到了,就使用它來(lái)創(chuàng)建類Foo,如果沒有找到,就使用type來(lái)創(chuàng)建類。
所以對(duì)于以下代碼:

class Foo(Bar):
  pass

Python工作流程如下:

首先檢查Foo中是否具有屬性__metaclass__?

如果找到,就使用__metaclass__定義的元類在內(nèi)存中創(chuàng)建一個(gè)類對(duì)象。

如果在類定義中沒有找到這個(gè)屬性,就在模塊級(jí)別中進(jìn)行查找。

如果還是沒有找到,就會(huì)使用父類Bar中的元類來(lái)創(chuàng)建類。

注意:類中的__metaclass__屬性不會(huì)被子類繼承,但是父類中的__class__會(huì)被繼承。

自定義元類

元類的主要作用是在創(chuàng)建類的時(shí)候自動(dòng)改變類。
例如,想要實(shí)現(xiàn)模塊中所有的類屬性都是大寫格式。可以定義模塊級(jí)別的__metaclass__來(lái)實(shí)現(xiàn)。
這樣模塊中所有的類都是通過(guò)這個(gè)元類來(lái)創(chuàng)建的。

def upper_attr(future_class_name, future_class_parents, future_class_attr):
  """
    返回一個(gè)類,該類的所有屬性名的都為大寫
  """
  # 將不是__開頭的屬性名轉(zhuǎn)為大寫字母
  uppercase_attr = {}
  for name, val in future_class_attr.items():
      if not name.startswith("__"):
          uppercase_attr[name.upper()] = val
      else:
          uppercase_attr[name] = val
  # 使用type創(chuàng)建類
  return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # 定義模塊級(jí)別的元類,這樣模塊中所有類都會(huì)使用該元類創(chuàng)建

class Foo(): 
  # 注意,新式類不支持模塊級(jí)別的元類,但是可以在類中定義__metaclass__
  bar = "bip"

print(hasattr(Foo, "bar"))
# 輸出: False
print(hasattr(Foo, "BAR"))
# 輸出: True

f = Foo()
print(f.BAR)
# Out: "bip"

也可以將metaclass定義為一個(gè)真正的類:

# 記住type還是一個(gè)類,所以可以繼承它
class UpperAttrMetaclass(type): 
    # __new__ 會(huì)在__init__之前調(diào)用,它會(huì)創(chuàng)建并返回一個(gè)實(shí)例
    # 而__init__僅用于初始化,進(jìn)行一些參數(shù)的配置 
    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的思想,因?yàn)樗苯诱{(diào)用了type方法,實(shí)際上可以調(diào)用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
        # 調(diào)用type.__new__方法 
        return type.__new__(upperattr_metaclass, future_class_name, 
                            future_class_parents, uppercase_attr)

你可能注意到參數(shù)upperattr_metaclass, 它代表要實(shí)例化的類。當(dāng)然,我這里取這么個(gè)復(fù)雜的名字主要是為了明確它的含義。但是,就像self參數(shù)一樣,所有參數(shù)都有其習(xí)慣性命名。所以生產(chǎn)環(huán)境下的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方法,以便減輕這種繼承關(guān)系。

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)

元類實(shí)際上做了以下三方面的工作:

干涉創(chuàng)建類的過(guò)程

修改類

返回修改之后的類

為什么使用類而不是函數(shù)來(lái)定義元類?

理由如下:

目的更明確,當(dāng)你閱讀UpperAttrMetaclass(type)的時(shí)候,你知道它用來(lái)做什么。

可以使用面向?qū)ο缶幊?,元類可以繼承自其它元類,還可以覆蓋父類方法。

可以更好的組織代碼結(jié)構(gòu)。元類通常用于處理比較復(fù)雜的情況。

可以為__new__、__init____call__編寫鉤子,為后續(xù)開發(fā)者提供便利。

為什么使用元類?

現(xiàn)在,終極問(wèn)題來(lái)了,為什么要使用元類這種模糊且容易出錯(cuò)的功能?
一般情況下,我們并不會(huì)使用元類,99%的開發(fā)者并不會(huì)用到元類,所以一般不用考慮這個(gè)問(wèn)題。
元類主用用于創(chuàng)建API,一個(gè)典型的例子就是Django的ORM。
它讓我們可以這樣定義一個(gè)類:

class Person(models.Model):
  name = models.CharField(max_length=30)
  age = models.IntegerField()

運(yùn)行下面的代碼:

guy = Person(name="bob", age="35")
print(guy.age)

返回的結(jié)果是int類型而不是IntegerField對(duì)象。這是因?yàn)?b>models.Model使用了元類,它會(huì)將Python中定義的字段轉(zhuǎn)換成數(shù)據(jù)庫(kù)中的字段。
通過(guò)使用元類,Django將復(fù)雜的接口轉(zhuǎn)換成簡(jiǎn)單的接口。

總結(jié)

首先,我們知道了類其實(shí)就是可以創(chuàng)建實(shí)例的對(duì)象。而類又是通過(guò)元類來(lái)創(chuàng)建的。

>>> class Foo(object): pass
>>> id(Foo)
142630324

Python中所有數(shù)據(jù)類型都是對(duì)象,它們要么是類的實(shí)例要么是元類的實(shí)例。
除了type,它實(shí)際上是自身的元類。這一點(diǎn)沒法在Python中重現(xiàn),因?yàn)樗窃诰幾g階段實(shí)現(xiàn)的。

其次, 元類都是復(fù)雜的,對(duì)于一般的類是用不著的??梢允褂靡韵聝煞N技巧修改類:

monkey patch

類修飾器

當(dāng)你需要修改類的時(shí)候,99%的情況下可以使用元類。但是99%的情況下,你根本不需要修改一個(gè)類。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/38050.html

相關(guān)文章

  • 再有人問(wèn)什么元類,就把這篇文章扔給他!

    摘要:同時(shí),在元類中,我們還需要加上一個(gè)判斷,只有在這個(gè)類創(chuàng)建時(shí)才需要控制其類的生成,其他的就不需要了。完整代碼后臺(tái)回復(fù)元類獲取原創(chuàng)不易,如果文章對(duì)你有用的話,點(diǎn)贊留言轉(zhuǎn)發(fā)是對(duì)我的最大支持日常學(xué)代碼不止,還有美和樂(lè)趣 我之前在深入理解python中的類和對(duì)象中說(shuō)過(guò),python中的類也是一個(gè)對(duì)象,可以說(shuō)是類對(duì)象,可以由type來(lái)創(chuàng)建類對(duì)象的。有了這個(gè)知識(shí)我們先看看下面這個(gè)函數(shù): showIm...

    王巖威 評(píng)論0 收藏0
  • python 類和元類(metaclass)的理解和簡(jiǎn)單運(yùn)用

    摘要:什么是元類剛才說(shuō)了,元類就是創(chuàng)建類的類。類上面的屬性,相信愿意了解元類細(xì)節(jié)的盆友,都肯定見過(guò)這個(gè)東西,而且為之好奇。使用了這個(gè)魔法方法就意味著就會(huì)用指定的元類來(lái)創(chuàng)建類了。深刻理解中的元類 (一) python中的類 今天看到一篇好文,然后結(jié)合自己的情況總結(jié)一波。這里討論的python類,都基于python2.7x以及繼承于object的新式類進(jìn)行討論。 首先在python中,所有東西都...

    zhangqh 評(píng)論0 收藏0
  • Python -- 元類metaclass詳解

    摘要:原鏈接中的元類是什么類也是對(duì)象在理解元類之前,需要掌握中類概念。事實(shí)上,是中用于創(chuàng)建所有類的元類。類本身是元類的對(duì)象在中,除了,一切皆對(duì)象,一切都是類或者元類的對(duì)象。事實(shí)上是自己的元類, 學(xué)習(xí)契機(jī) 項(xiàng)目中使用Elasticsearch(ES)存儲(chǔ)海量業(yè)務(wù)數(shù)據(jù),基于ES向外提供的API進(jìn)一層封裝,按需處理原始數(shù)據(jù)提供更精確、更多樣化的結(jié)果。在研究這一層的代碼時(shí)接觸到@six.add_me...

    tracy 評(píng)論0 收藏0
  • Python: 陌生的 metaclass

    摘要:但一般情況下,我們使用類作為元類。那么,元類到底有什么用呢要你何用元類的主要目的是為了控制類的創(chuàng)建行為。當(dāng)然,有很多種做法,這里展示用元類的做法。當(dāng)你創(chuàng)建類時(shí),解釋器會(huì)調(diào)用元類來(lái)生成它,定義一個(gè)繼承自的普通類意味著調(diào)用來(lái)創(chuàng)建它。 元類 Python 中的元類(metaclass)是一個(gè)深度魔法,平時(shí)我們可能比較少接觸到元類,本文將通過(guò)一些簡(jiǎn)單的例子來(lái)理解這個(gè)魔法。 類也是對(duì)象 在 Py...

    miya 評(píng)論0 收藏0
  • Python實(shí)例一個(gè)類背后發(fā)生了什么

    摘要:好吧,事實(shí)上,類本身也是實(shí)例,當(dāng)然,它們是元類的實(shí)例。中的一切都是對(duì)象,它們要么是類的實(shí)例,要么是元類的實(shí)例,除了。 寫在最前面 一些很重要的知識(shí),我的寫得有點(diǎn)亂,也可以去看這些文章 Python 面向?qū)ο螅ǔ跫?jí)篇) Python 面向?qū)ο螅ㄟM(jìn)階篇) 深刻理解Python中的元類(metaclass) 首先來(lái)看一個(gè)例子,正常情況下我們定義并且實(shí)例一個(gè)類如下 class Foo(ob...

    shusen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<