国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Python -- 元類metaclass詳解

tracy / 2023人閱讀

摘要:原鏈接中的元類是什么類也是對(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_metaclass(abc.ABCMeta),故而學(xué)習(xí)一下Python的元類。不過,雖然@six.add_metaclass(abc.ABCMeta)實(shí)現(xiàn)上與元類有關(guān),但實(shí)際應(yīng)用只需要調(diào)用其接口,并不需要接觸后幕后的元類操作。
翻譯這篇答案是為了方便自己記憶理解,其實(shí)原文中一些地方我自己不是很明白,所以這個(gè)翻譯會(huì)根據(jù)自己理解的程度持續(xù)更新。

原鏈接

stackoverflow-What are metaclasses in Python?

Python中的元類是什么 類也是對(duì)象

在理解元類之前,需要掌握Python中類概念。Python的類概念稍有奇特,其借鑒于Smalltalk。
在大部分語(yǔ)言中,類用于描述如何生成一個(gè)對(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解釋器執(zhí)行代碼時(shí)會(huì)生成一個(gè)對(duì)象。以下代碼會(huì)在內(nèi)存中創(chuàng)建一個(gè)名為“ObjectCreator”的對(duì)象:

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

這個(gè)對(duì)象(類)自身可以創(chuàng)建對(duì)象(實(shí)例),這是為什么它是類的原因。
不過它仍然是一個(gè)對(duì)象,你可以:

可以將它賦值給變量

可以復(fù)制

可以添加屬性 TODO 添加屬性只是對(duì)象的特性?

可以將其當(dāng)作函數(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ì)象,那就可像其他對(duì)象那樣,動(dòng)態(tài)創(chuàng)建類。
首先,可以使用關(guān)鍵字class,在函數(shù)中創(chuàng)建類:

>>> 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),因?yàn)檫€是需要自己編寫整個(gè)類的代碼。所以,想一想,這些類既然是對(duì)象,就必然是某種東西生成的。當(dāng)使用class關(guān)鍵字時(shí),Python自動(dòng)創(chuàng)建了類這個(gè)對(duì)象,但是像Python中大部分事情一樣,Python中也可以手動(dòng)創(chuàng)建類。
還記type方法嗎?一個(gè)可以讓你知道一個(gè)對(duì)象是什么類型的方法:

>>> print(type(1))

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

>>> print(type(ObjectCreator))

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

除此之外,type還有一個(gè)完全不同的功能:動(dòng)態(tài)創(chuàng)建類。將類的描述作為參數(shù)傳遞給type,會(huì)返回一個(gè)類。(我知道,同一個(gè)函數(shù)根據(jù)傳參的不同而展示出兩種完全不同的功能很不合理,不過這是為了Python的向后兼容。)
type如何創(chuàng)建類:

type(類名,
     父類元祖 (可為空),
     包含鍵值對(duì)屬性的字典)

舉例:

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

上面這個(gè)MyShinyClass類可以用以下方法手動(dòng)創(chuàng)建:

>>> MyShinyClass = type("MyShinyClass", (), {}) # 返回一個(gè)類
>>> print(MyShinyClass)

>>> print(MyShinyClass()) # 創(chuàng)建一個(gè)類對(duì)象
<__main__.MyShinyClass object at 0x8997cec>

應(yīng)該注意到了,使用"MyShinyClass"作為類名,也將其作為一個(gè)變量名,賦值為類的引用。類名和變量名是可以不同的,但是沒必要把事情搞復(fù)雜。
type可以接受一個(gè)定義類屬性的字典作為參數(shù):

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

以上定義等同于:

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

使用起來(lái)跟一個(gè)普通類一樣:

>>> print(Foo)

>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)  # 利用實(shí)例打印類屬性
True

當(dāng)然,也可以作為基類,給其他類繼承:

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

以上代碼等同于:

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

>>> print(FooChild.bar) # bar屬性繼承自類Foo
True

你肯定還想為類添加方法。只需要定義一個(gè)名稱合理的函數(shù),并將這個(gè)函數(shù)名作為屬性傳遞給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

在動(dòng)態(tài)創(chuàng)建一個(gè)類之后,為這個(gè)類添加更多的方法,就像為一個(gè)正常創(chuàng)建的類添加方法一樣:

>>> def echo_bar_more(self):
...       print("yet another method")
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, "echo_bar_more")
True

現(xiàn)在已經(jīng)看到:在Python中,類也是對(duì)象,可以動(dòng)態(tài)地創(chuàng)建類。當(dāng)使用關(guān)鍵字class時(shí),Python使用元類像這樣創(chuàng)建類的。

什么是元類(終于講到了)

元類就是創(chuàng)建類的“東西”。我們定義類是為了創(chuàng)建對(duì)象,是吧?但是我們認(rèn)識(shí)到在Python中類也是對(duì)象,而元類就是創(chuàng)建類這種對(duì)象(類)的,它們是類的類,你可以這樣理解:

MyClass = MetaClass()
MyObject = MyClass()

你已經(jīng)看到type可以讓你做如下操作:

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

這是因?yàn)閠ype方法實(shí)際上是一個(gè)元類。事實(shí)上,type是Python中用于創(chuàng)建所有類的元類。不過現(xiàn)在你一定很奇怪為什么這個(gè)類名首字母是小寫,而不是Type?我猜這是同str保持一致,str是用來(lái)創(chuàng)建string對(duì)象的類,int是用來(lái)創(chuàng)建integer對(duì)象的類,type則是創(chuàng)建類對(duì)象的類。
在Python中,一切,對(duì)就是一切,都是對(duì)象。包括整數(shù)、字符串、函數(shù)和類。所有東西都是對(duì)象,它們都是創(chuàng)建自某個(gè)類。
通過__class__屬性可以驗(yàn)證這一點(diǎn):

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

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

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

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

那么,一個(gè)__class__的__class__屬性是什么呢?

>>> age.__class__.__class__

>>> name.__class__.__class__

>>> foo.__class__.__class__

>>> b.__class__.__class__

由此可見:元類確實(shí)就是創(chuàng)建類對(duì)象的東西。如果你覺得合適你就可以稱之為“類工廠”。type是Python使用的內(nèi)置元類,當(dāng)然,你也可以自己創(chuàng)建元類。

__metaclass__屬性

編寫一個(gè)類時(shí)添加上__metaclass__屬性:

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

如果你像上面這樣做,Python就會(huì)使用元類創(chuàng)建一個(gè)Foo類。
要當(dāng)心了,這里有些小圈套。你先寫下了“class Foo(object)”,但此時(shí)內(nèi)存中還沒有創(chuàng)建Foo類對(duì)象。Python會(huì)在類的聲明中尋找屬性__metaclass_,如果找到了就會(huì)使用其創(chuàng)建Foo類;如果沒有,會(huì)使用type創(chuàng)建這個(gè)類。下面這段文字要多讀幾遍。
當(dāng)你編寫以下代碼時(shí):

class Foo(Bar):
    pass

Python做了這些事情:

在類Foo中有定義__metaclass__屬性嗎?
如果有,則繼續(xù);
如果沒有,Python會(huì)在模塊層尋找__metaclass__屬性(這只針對(duì)沒有繼承任何其他類的情況);
如果模塊層也沒有,則會(huì)在Bar(第一個(gè)父類)中尋找(這就有可能是內(nèi)置的type)。
這樣找到__metaclass__后,使用它在內(nèi)存中創(chuàng)建名稱為Foo的類對(duì)象(這邊跟上,一個(gè)類對(duì)象)

需要注意的是,__metaclass__屬性不會(huì)被繼承,但是父類的元類(Bar.__class__)可以被繼承:如果Bar的__metaclass__屬性定義了使用type()(不是type.__new())創(chuàng)建Bar類,其子類不會(huì)繼承這個(gè)行為。(Be careful here that the metaclass attribute will not be inherited, the metaclass of the parent (Bar.__class__) will be. If Bar used a metaclass attribute that created Bar with type() (and not type.__new__()), the subclasses will not inherit that behavior.)TODO 這邊不太理解
現(xiàn)在有個(gè)新問題,你可以賦什么值給__metaclass__?
答案是:可以創(chuàng)建一個(gè)類的東西。
那什么可以創(chuàng)建類?type、子類化type或者使用type的東西。

自定義元類

元類的主要目的,是在創(chuàng)建類的時(shí)候動(dòng)態(tài)地改變類。通常你會(huì)想創(chuàng)建符合當(dāng)前上下文的類供API使用。舉一個(gè)簡(jiǎn)單的例子,當(dāng)你希望一個(gè)模塊中所有的類屬性都是小寫時(shí),有幾種方法可以實(shí)現(xiàn),其中有一種方法就是在模塊層設(shè)置__metaclass__。使用這種方法,這個(gè)模塊中所有的類都將使用此元類創(chuàng)建,我們只需要使元類將所有類屬性置為小寫。
幸運(yùn)的是,__metaclass__可以被任意調(diào)用,不一定非要是一個(gè)正式的類(我知道,名稱包含“class”不一定非要是一個(gè)類,搞清楚了...這很有用)。
現(xiàn)在先用一個(gè)函數(shù)舉一個(gè)簡(jiǎn)單的例子:

# 元類會(huì)自動(dòng)獲取通常傳給`type`的參數(shù)
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      返回一個(gè)類對(duì)象,將其屬性置為大寫
    """

    # 過濾出所有開頭不為"__"的屬性,置為大寫
    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  # 這會(huì)影響此模塊中所有的類

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"

現(xiàn)在,完成同樣的功能,但是為元類定義一個(gè)真實(shí)的類:

# 記住`type`實(shí)際上是一個(gè)像`str`和`int`的類,可以用于被繼承
class UpperAttrMetaclass(type):
    # __new__放在__init__之前調(diào)用,此方法創(chuàng)建對(duì)象并反回
    # 而__init__則是初始化作為參數(shù)傳遞給此方法的對(duì)象
    # 除非你想控制如何創(chuàng)建一個(gè)對(duì)象,否則很少用到__new__
    # 在這里,被創(chuàng)建的對(duì)象是類,而我們想自定義這個(gè)類,所以重寫了__new__
    # 如果需要的話你也可以在__init__中做一些操作
    # 一些高級(jí)用法會(huì)包括重寫__call__,不過這里還不需要
    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)

這不是真正的面向?qū)ο螅∣OP),這里直接調(diào)用了type,沒有重寫或者調(diào)用父類的__new__。現(xiàn)在像這樣處理:

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

        # 復(fù)用type.__new__方法
        # 這是基本的OOP,沒什么深?yuàn)W的
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

你大概發(fā)現(xiàn)了傳給type的額外的參數(shù)upperattr_metaclass。這沒什么奇怪的:__new__的第一個(gè)參數(shù)總是其定義的類。就像類方法中第一個(gè)參數(shù)總是self。當(dāng)然,為了清晰期間,這里我起的名字比較長(zhǎng),但是像self這樣的參數(shù)通常有一個(gè)傳統(tǒng)的名字。所以真正的產(chǎ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 type.__new__(cls, clsname, bases, uppercase_attr)

使用super可以更清晰,which will ease inheritance (because yes, you can have metaclasses, inheriting from metaclasses, inheriting from type)TODO:

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)

以上,關(guān)于元類也沒有更多了。使用元類的代碼比較復(fù)雜的原因不在于元類,而在于你通常會(huì)依靠自省、操縱繼承、__dict__變量等,使用元類做一些晦澀的事情。(it"s because you usually use metaclasses to do twisted stuff relying on introspection, manipulating inheritance, vars such as __dict__, etc.)TODO
元類來(lái)用于黑魔法時(shí)的確特別有用,因?yàn)橐矔?huì)將事情搞得很復(fù)雜。但就其本身而言,是簡(jiǎn)單的:

攔截一個(gè)類的創(chuàng)建

修改類

返回修改的類

為什么會(huì)用元類代替函數(shù)?

既然__metaclass__可以被任意調(diào)用,為什么要使用明顯更復(fù)雜的類呢?有這樣一些理由:

意圖明顯。當(dāng)你看到UpperAttrMetaclass(type),你知道接下來(lái)會(huì)發(fā)生什么。

可以使用OOP。元類可以繼承自元類,重寫父類的方法,元類甚至可以使用元類。

如果為一個(gè)類指定的元類是類而不是方法,這個(gè)類的子類將是元類的一個(gè)實(shí)例。Children of a class will be instances of its metaclass if you specified a metaclass-class, but not with a metaclass-function.TODO

你可以將代碼組織得更好。使用元類時(shí)肯定不會(huì)僅想像上面舉的例子那樣簡(jiǎn)單,通常是用于比較復(fù)雜的場(chǎng)景。將多個(gè)方法組織在一個(gè)類中有益于使代碼更容易閱讀。

你可以使用__new__,__init__ 和 __call__,這些方法可以處理不用的事情。即使很多時(shí)候你可以在__new__中完成所有工作,當(dāng)然,一些人會(huì)更習(xí)慣用__init__。

這些東西叫 “metaclass”,小心了!這一定很難搞!

為什么使用元類

好了,現(xiàn)在的問題是:為什么要是用這樣晦澀且容易出錯(cuò)的特性?其實(shí),通常你不會(huì)用:

元類是99%的用戶根本不必操心的深度魔法。如果你在考慮是否需要使用,那就不要用(真正需要的用戶很清楚他們的需求,根本不需要解釋為什么要使用元類) 
Python領(lǐng)袖 Tim Peters

使用元類的一個(gè)主要場(chǎng)景是創(chuàng)建API。Django ORM是一個(gè)典型的例子,它允許你像下面這樣定義:

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

如果你這樣做:

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

它不會(huì)返回一個(gè)IntegerField的對(duì)象,而是返回一個(gè)整數(shù),甚至可以從數(shù)據(jù)庫(kù)中直接獲取數(shù)據(jù)。TODO
這是因?yàn)椋趍odels.Model中定義了__metaclass__,使用了一些魔法將你定義的簡(jiǎn)單的Person類轉(zhuǎn)換為一個(gè)復(fù)雜的數(shù)據(jù)庫(kù)掛鉤。(turn the Person you just defined with simple statements into a complex hook to a database field.)TODO

Django使用元類對(duì)外提供簡(jiǎn)單的API,簡(jiǎn)化了一些復(fù)雜的東西,API中重建的代碼會(huì)去完成幕后真正的工作。

結(jié)語(yǔ)

首先,類是可以創(chuàng)建實(shí)例的對(duì)象。類本身是元類的對(duì)象:

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

在Python中,除了type,一切皆對(duì)象,一切都是類或者元類的對(duì)象。事實(shí)上type是自己的元類,這在純Python中這是無(wú)法實(shí)現(xiàn)的,這里在實(shí)現(xiàn)層上做了一些手段。
其次,元類是復(fù)雜的。你可能不希望對(duì)非常簡(jiǎn)單的類使用元類,那么還有其他兩種手段用來(lái)改變類:

monkey patching

類裝飾器

如果你需要改變類,99%的情況下使用這兩種方法。
但其實(shí)98%的情況你根本不需要改變類。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/44599.html

相關(guān)文章

  • How does it work - with_metaclass

    摘要:先簡(jiǎn)單介紹下中的元類。元類就是創(chuàng)建類的類,對(duì)于元類來(lái)說,類是它的實(shí)例,將返回。中的所有類,都是的實(shí)例,換句話說,是元類的基類。 我在看源代碼的時(shí)候,經(jīng)常蹦出這一句:How does it work!竟然有這種操作?本系列文章,試圖剖析代碼中發(fā)生的魔法。順便作為自己的閱讀筆記,以作提高。 先簡(jiǎn)單介紹下Python中的元類(metaclass)。元類就是創(chuàng)建類的類,對(duì)于元類來(lái)說,類是它的實(shí)...

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

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

    zhangqh 評(píng)論0 收藏0
  • [譯]什么是元類metaclass?

    摘要:如果還是沒有找到,就會(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中類的概念與SmallT...

    zsirfs 評(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í)我們可能比較少接觸到元類,本文將通過一些簡(jiǎn)單的例子來(lái)理解這個(gè)魔法。 類也是對(duì)象 在 Py...

    miya 評(píng)論0 收藏0
  • 由type()函數(shù)對(duì)類和實(shí)例使用結(jié)果差異而引出的一個(gè)問題

    摘要:但是隨后有人提出反對(duì)意見并說這個(gè)是隨后搜索到這篇文章深刻理解中的元類里面介紹了如何使用函數(shù)創(chuàng)建一個(gè)類,并解釋了屬性。 有如下代碼 #-*-coding:utf-8-*- class a(): pass a1 = a() print(type(a),type(a1)) 兩個(gè)python版本分別為Python2.7.11Python3.5.1 在python2中得到的結(jié)果(, )a...

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

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

0條評(píng)論

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