亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Python “黑魔法” 之 Meta Classes

LeoHsiun / 2133人閱讀

摘要:幸而,提供了造物主的接口這便是,或者稱為元類。接下來(lái)我們將通過(guò)一個(gè)栗子感受的黑魔法,不過(guò)在此之前,我們要先了解一個(gè)語(yǔ)法糖。此外,在一些小型的庫(kù)中,也有元類的身影。

首發(fā)于 我的博客 轉(zhuǎn)載請(qǐng)注明出處

接觸過(guò) Django 的同學(xué)都應(yīng)該十分熟悉它的 ORM 系統(tǒng)。對(duì)于 python 新手而言,這是一項(xiàng)幾乎可以被稱作“黑科技”的特性:只要你在models.py中隨便定義一個(gè)Model的子類,Django 便可以:

獲取它的字段定義,并轉(zhuǎn)換成表結(jié)構(gòu)

讀取Meta內(nèi)部類,并轉(zhuǎn)化成相應(yīng)的配置信息。對(duì)于特殊的Model(如abstract、proxy),還要進(jìn)行相應(yīng)的轉(zhuǎn)換

為沒(méi)有定義objectsModel加上一個(gè)默認(rèn)的Manager

開(kāi)發(fā)之余,我也曾腦補(bǔ)過(guò)其背后的原理。曾經(jīng),我認(rèn)為是這樣的:

啟動(dòng)時(shí),遍歷models.py中的所有屬性,找到Model的子類,并對(duì)其進(jìn)行上述的修改。

當(dāng)初,我還以為自己觸碰到了真理,并曾將其應(yīng)用到實(shí)際生產(chǎn)中——為 SAE 的 KVDB 寫(xiě)了一個(gè)類 ORM 系統(tǒng)。然而在實(shí)現(xiàn)的過(guò)程中,我明顯感受到了這種方法的丑陋,而且性能并不出色(因?yàn)橐闅v所有的定義模塊)。

那么事實(shí)上,Django 是怎么實(shí)現(xiàn)的呢?

自古以來(lái)我們制造東西的方法都是“自上而下”的,是用切削、分割、組合的方法來(lái)制造。然而,生命是自下而上地,自發(fā)地建造起來(lái)的,這個(gè)過(guò)程極為低廉。
——王晉康 《水星播種》

這句話揭示了生命的神奇所在:真正的生命都是由基本物質(zhì)自發(fā)構(gòu)成的,而非造物主流水線式的加工。

那么,如果 類 也有生命的話,對(duì)它自己的修飾就不應(yīng)該由調(diào)用者來(lái)完成,而應(yīng)該是自發(fā)的

幸而,python 提供了造物主的接口——這便是 Meta Classes,或者稱為“元類”。

元類 是什么?

簡(jiǎn)單說(shuō):元類就是類的類。

首先,要有一個(gè)概念:

python 中,一切都是對(duì)象。

沒(méi)錯(cuò),一切,包括 類 本身。

既然,類 是 對(duì)象,對(duì)象 是 類的實(shí)例,那么——類 也應(yīng)該有 類 才對(duì)。

類的類:type

在 python 中,我們可以用type檢測(cè)一個(gè)對(duì)象的類,如:

print type(1) # 

如果對(duì)一個(gè)類操作呢?

print type(int) # 

class MyClass(object): pass

print type(MyClass) # 

print type(type) # 

這說(shuō)明:type其實(shí)是一個(gè)類型,所有類——包括type自己——的類都是type。

type 簡(jiǎn)介

從 官方文檔 中,我們可以知道:

dict 類似,type 也是一個(gè)工廠構(gòu)造函數(shù),調(diào)用其將返回 一個(gè)type類型的實(shí)例(即 類)。

type 有兩個(gè)重載版本:

type(object),即我們最常用的版本。

type(name, bases, dict),一個(gè)更強(qiáng)大的版本。通過(guò)指定 類名稱(name)、父類列表(bases)和 屬性字典(dict) 動(dòng)態(tài)合成一個(gè)類。

下面兩個(gè)語(yǔ)句等價(jià):

class Integer(int):

   name = "my integer"

   def increase(self, num):
       return num + 1

    # -------------------

    Integer = type("Integer", (int, ), {
   "name": "my integer",
   "increase": lambda self, num: 
                   num + 1    # 很酷的寫(xiě)法,不是么
    })

也就是說(shuō):類的定義過(guò)程,其實(shí)是type類型實(shí)例化的過(guò)程。

然而這和修飾一個(gè)已定義的類有什么關(guān)系呢?

當(dāng)然有啦~既然“類的定義”就是“type類型的初始化過(guò)程”,那其中必定會(huì)調(diào)用到type的構(gòu)造函數(shù)(__new__()__init__())。只要我們繼承 type類 并修改其 __new__函數(shù),在這里面動(dòng)手腳就可以啦。

接下來(lái)我們將通過(guò)一個(gè)栗子感受 python 的黑魔法,不過(guò)在此之前,我們要先了解一個(gè)語(yǔ)法糖。

__metaclass__ 屬性

有沒(méi)覺(jué)得上面第二段示例有些鬼畜呢?它勒令程序員將類的成員寫(xiě)成一個(gè)字典,簡(jiǎn)直是反人類。如果我們真的是要通過(guò)修改 元類 來(lái)改變 類 的行為的話,似乎就必須采用這種方法了~~簡(jiǎn)直可怕~~

好在,python 2.2 時(shí)引進(jìn)了一個(gè)語(yǔ)法糖:__metaclass__。

class Integer(int):

    __metaclass__ = IntMeta

現(xiàn)在將會(huì)等價(jià)于:

Integer = IntMeta("Integer", (int, ), {})

由此一來(lái),我們?cè)谑褂脗鹘y(tǒng)類定義的同時(shí),也可以使用元類啦。

栗子:子類凈化器
需求描述

你是一個(gè)有語(yǔ)言潔癖的開(kāi)發(fā)者,平時(shí)容不得別人講一句臟話,在開(kāi)發(fā)時(shí)也是如此?,F(xiàn)在,你寫(xiě)出了一個(gè)非常棒的框架,并馬上要將它公之于眾了。不過(guò),你的強(qiáng)迫癥又犯了:如果你的使用者在代碼中寫(xiě)滿了臟話,怎么辦?豈不是玷污了自己的純潔?

假如你就是這個(gè)喪心病狂的開(kāi)發(fā)者,你會(huì)怎么做?

在知道元類之前,你可能會(huì)無(wú)從下手。不過(guò),這個(gè)問(wèn)題你可以用 元類 輕松解決——只要在類定義時(shí)過(guò)濾掉不干凈的字眼就好了(百度貼吧的干活~~)。

我們的元類看起來(lái)會(huì)是這樣的:

sensitive_words_list = ["asshole", "fuck", "shit"]

def detect_sensitive_words(string):
    """檢測(cè)敏感詞匯"""
    words_detected = filter(lambda word: word in string.lower(), sensitive_words_list)

    if words_detected:
        raise NameError("Sensitive words {0} detected in the string "{1}"." 
            .format(
                ", ".join(map(lambda s: ""%s"" % s, words_detected)),
                string
            )
        )

class CleanerMeta(type):

    def __new__(cls, class_name, bases, attrs):
        detect_sensitive_words(class_name) # 檢查類名
        map(detect_sensitive_words, attrs.iterkeys()) # 檢查屬性名

        print "Well done! You are a polite coder!" # 如無(wú)異常,輸出祝賀消息

        return super(CleanerMeta, cls).__new__(cls, class_name, bases, attrs)
        # 重要!這行一定不能漏??!這回調(diào)用內(nèi)建的類構(gòu)造器來(lái)構(gòu)造類,否則定義好的類將會(huì)變成 None

現(xiàn)在,只需這樣定義基類:

class APIBase(object):

    __metaclass__ = CleanerMeta

    # ...

那么所有 APIBase 的派生類都會(huì)接受安全審查(奸笑~~):

class ImAGoodBoy(APIBase):

    a_polite_attribute = 1

# [Output] Well done! You are a polite coder!

class FuckMyBoss(APIBase):

    pass

# [Output] NameError: Sensitive words "fuck" detected in the string "FuckMyBoss".

class PretendToBePolite(APIBase):

    def __fuck_your_asshole(self):
        pass

# [Output] NameError: Sensitive words "asshole", "fuck" detected in the string "_PretendToBePolite__fuck_your_asshole".

看,即使像最后一個(gè)例子中的私有屬性也難逃審查,因?yàn)樗鼈儽举|(zhì)都是相同的。

甚至,你還可以對(duì)有問(wèn)題的屬性進(jìn)行偷偷的修改,比如 讓不文明的函數(shù)在調(diào)用時(shí)打出一行警告 等等,這里就不多說(shuō)了。

元類 在實(shí)際開(kāi)發(fā)中的應(yīng)用

日常開(kāi)發(fā)時(shí),元類 常用嗎?

當(dāng)然,Django 的 ORM 就是一個(gè)例子,大名鼎鼎的 SQLAlchemy 也用了這種黑魔法。

此外,在一些小型的庫(kù)中,也有 元類 的身影。比如 abc(奇怪的名字~~)——這是 python 的一個(gè)內(nèi)建庫(kù),用于模擬 抽象基類(Abstract Base Classes)。開(kāi)發(fā)者可以使用 abc.abstractmethod 裝飾器,將 指定了 __metaclass__ = abc.ABCMeta 的類的方法定義成 抽象方法,同時(shí)這個(gè)類也成了 抽象基類,抽象基類是不可實(shí)例化的。這便實(shí)現(xiàn)了對(duì) 抽象基類 的模擬。

倘若你也有需要?jiǎng)討B(tài)修改類定義的需求,不妨也試試這種“黑魔法”。

小結(jié)

類 也是 對(duì)象,所有的類都是type的實(shí)例

元類(Meta Classes)是類的類

__metaclass__ = MetaMeta(name, bases, dict) 的 語(yǔ)法糖

可以通過(guò)重載元類的 __new__ 方法,修改 類定義 的行為

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

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

相關(guān)文章

  • Python魔法 Encoding & Decoding

    摘要:我可以明確告訴你這不是,但它可以用解釋器運(yùn)行。這種黑魔法,還要從說(shuō)起。提案者設(shè)想使用一種特殊的文件首注釋,用于指定代碼的編碼。暴露了一個(gè)函數(shù),用于注冊(cè)自定義編碼。所謂的黑魔法其實(shí)并不神秘,照貓畫(huà)虎定義好相應(yīng)的接口即可。 首發(fā)于我的博客,轉(zhuǎn)載請(qǐng)注明出處 寫(xiě)在前面 本文為科普文 本文中的例子在 Ubuntu 14.04 / Python 2.7.11 下運(yùn)行成功,Python 3+ 的接...

    鄒強(qiáng) 評(píng)論0 收藏0
  • 徒手?jǐn)]一個(gè) Spring Boot 中的 Starter ,解密自動(dòng)化配置魔法!

    摘要:先來(lái)看代碼吧,一會(huì)松哥再慢慢解釋關(guān)于這一段自動(dòng)配置,解釋如下首先注解表明這是一個(gè)配置類。本文的案例,松哥已經(jīng)上傳到上了,地址。我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中。Starter 為我們帶來(lái)了眾多的自動(dòng)化配置,有了這些自動(dòng)化配置,我們可以不費(fèi)吹灰之力就能搭建一個(gè)生產(chǎn)級(jí)開(kāi)發(fā)環(huán)境,有的小伙伴會(huì)覺(jué)得這個(gè) Starter 好神奇呀!其實(shí) Starter 也都...

    xiaochao 評(píng)論0 收藏0
  • Python魔法 Generator Coroutines

    摘要:主程序通過(guò)喚起子程序并傳入數(shù)據(jù),子程序處理完后,用將自己掛起,并返回主程序,如此交替進(jìn)行。通過(guò)輪詢或是等事件框架,捕獲返回的事件。從消息隊(duì)列中取出記錄,恢復(fù)協(xié)程函數(shù)。然而事實(shí)上只有直接操縱的協(xié)程函數(shù)才有可能接觸到這個(gè)對(duì)象。 首發(fā)于 我的博客 轉(zhuǎn)載請(qǐng)注明出處 寫(xiě)在前面 本文默認(rèn)讀者對(duì) Python 生成器 有一定的了解,不了解者請(qǐng)移步至生成器 - 廖雪峰的官方網(wǎng)站。 本文基于 Pyth...

    李文鵬 評(píng)論0 收藏0
  • 從hello world看JavaScript隱藏的魔法

    摘要:如果類型轉(zhuǎn)換你還不是很了解,可以先讀下這篇來(lái)理解一下從看隱式強(qiáng)制轉(zhuǎn)換機(jī)制。函數(shù)可對(duì)通過(guò)編碼的字符串進(jìn)行解碼。而作者封裝的也是基于這兩者來(lái)實(shí)現(xiàn)輸出黑魔法字符串的。同時(shí)通過(guò),返回了一個(gè)匿名函數(shù)形成了閉包。為了達(dá)到裝逼的效果。 寫(xiě)在最前 事情的起因是這段看起來(lái)不像代碼的代碼: showImg(https://segmentfault.com/img/remote/14600000126810...

    cnio 評(píng)論0 收藏0
  • 經(jīng)驗(yàn)拾憶(純手工)=> Python__魔法__

    摘要:類的繼承類繼承有三種調(diào)用方式,其實(shí)是有區(qū)別的,聽(tīng)我慢慢道來(lái)第一種父類方法參數(shù)直接調(diào)用第二種方法參數(shù)直接調(diào)用在誰(shuí)的類下調(diào)用,就找此類對(duì)應(yīng)的下一個(gè)就是要繼承的第三種方法參數(shù)找類名對(duì)應(yīng)的的下一個(gè),就是繼承的,一般寫(xiě)本身的類名上下文管理器上下文管理 類的繼承 類繼承有三種調(diào)用方式,其實(shí)是 有區(qū)別 的,聽(tīng)我慢慢道來(lái) class A: def say(self, name): ...

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

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

0條評(píng)論

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