Python設(shè)計(jì)模式之裝飾器模式

裝飾器模式

無論何時(shí)我們想對(duì)一個(gè)對(duì)象添加額外的功能,都有下面這些不同的可選方法。

  • 如果合理,可以直接將功能添加到對(duì)象所屬的類(例如,添加一個(gè)新的方法)
  • 使用組合
  • 使用繼承

注意,本文中的Decorator可以為裝飾器或者修飾器。

與繼承相比,通常應(yīng)該優(yōu)先選擇組合,因?yàn)槔^承使得代碼更難復(fù)用,繼承關(guān)系是靜態(tài)的,并且應(yīng)用于整個(gè)類以及這個(gè)類的所有實(shí)例(請(qǐng)參考[GOF95,第31頁]和網(wǎng)頁[t.cn/RqrC8Yo])。

設(shè)計(jì)模式為我們提供第四種可選方法,以支持動(dòng)態(tài)地(運(yùn)行時(shí))擴(kuò)展一個(gè)對(duì)象的功能,這種方法就是修飾器。修飾器(Decorator)模式能夠以透明的方式(不會(huì)影響其他對(duì)象)動(dòng)態(tài)地將功能添加到一個(gè)對(duì)象中(請(qǐng)參考[GOF95,第196頁])。

在許多編程語言中,使用子類化(繼承)來實(shí)現(xiàn)修飾器模式(請(qǐng)參考[GOF95,第198頁])。在Python中,我們可以(并且應(yīng)該)使用內(nèi)置的修飾器特性。一個(gè)Python修飾器就是對(duì)Python語法的一個(gè)特定改變,用于擴(kuò)展一個(gè)類、方法或函數(shù)的行為,而無需使用繼承。從實(shí)現(xiàn)的角度來說,Python修飾器是一個(gè)可調(diào)用對(duì)象(函數(shù)、方法、類),接受一個(gè)函數(shù)對(duì)象fin作為輸入,并返回另一個(gè)函數(shù)對(duì)象fout(請(qǐng)參考網(wǎng)頁)。這意味著可以將任何具有這些屬性的可調(diào)用對(duì)象當(dāng)作一個(gè)修飾器。在第1章和第2章中已經(jīng)看到如何使用內(nèi)置的property修飾器讓一個(gè)方法表現(xiàn)為一個(gè)變量。在5.4節(jié),我們將學(xué)習(xí)如何實(shí)現(xiàn)及使用我們自己的修飾器。

修飾器模式和Python修飾器之間并不是一對(duì)一的等價(jià)關(guān)系。Python修飾器能做的實(shí)際上比修飾器模式多得多,其中之一就是實(shí)現(xiàn)修飾器模式(請(qǐng)參考[Eckel08,第59頁]和網(wǎng)頁[t.cn/RqrlLcQ])。

#!/usr/bin/env python
"""https://docs.python.org/2/library/functools.html#functools.wraps"""
"""https://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python/739665#739665"""

from functools import wraps


def makebold(fn):
    return getwrapped(fn, "b")


def makeitalic(fn):
    return getwrapped(fn, "i")


def getwrapped(fn, tag):
    @wraps(fn)
    def wrapped():
        return "<%s>%s</%s>" % (tag, fn(), tag)
    return wrapped


@makebold
@makeitalic
def hello():
    """a decorated hello world"""
    return "hello world"

if __name__ == '__main__':
    print('result:{}   name:{}   doc:{}'.format(hello(), hello.__name__, hello.__doc__))

### OUTPUT ###
# result:<b><i>hello world</i></b>   name:hello   doc:a decorated hello world
result:<b><i>hello world</i></b>   name:hello   doc:a decorated hello world
# http://stackoverflow.com/questions/3118929/implementing-the-decorator-pattern-in-python


class foo(object):
    def f1(self):
        print("original f1")

    def f2(self):
        print("original f2")


class foo_decorator(object):
    def __init__(self, decoratee):
        self._decoratee = decoratee

    def f1(self):
        print("decorated f1")
        self._decoratee.f1()

    def __getattr__(self, name):
        return getattr(self._decoratee, name) # 這個(gè)不是delegation么

u = foo()
v = foo_decorator(u)
v.f1()
v.f2()


decorated f1
original f1
original f2

現(xiàn)實(shí)中的例子

該模式雖名為修飾器,但這并不意味著它應(yīng)該只用于讓產(chǎn)品看起來更漂亮。修飾器模式通常用于擴(kuò)展一個(gè)對(duì)象的功能。這類擴(kuò)展的實(shí)際例子有,給槍加一個(gè)消音器、使用不同的照相機(jī)鏡頭(在可拆卸鏡頭的照相機(jī)上)等。

下圖由sourcemaking.com提供,展示了我們可以如何使用一些專用配件來修飾一把槍,使其 無聲、更準(zhǔn)以及更具破壞力(請(qǐng)參考網(wǎng)頁[t.cn/RqrC8Yo])。注意,圖中使用了子類化,但是在 Python中,這并不是必需的,因?yàn)榭梢允褂谜Z言內(nèi)置的修飾器特性。

軟件中的例子

Django框架大量地使用修飾器,其中一個(gè)例子是視圖修飾器。Django的視圖(View)修飾器 可用于以下幾種用途(請(qǐng)參考網(wǎng)頁[t.cn/RqrlJbA])。

  • 限制某些HTTP請(qǐng)求對(duì)視圖的訪問控制特定視圖上的緩存行為
  • 按單個(gè)視圖控制壓縮
  • 基于特定HTTP請(qǐng)求頭控制緩存

Grok框架也使用修飾器來實(shí)現(xiàn)不同的目標(biāo),比如下面幾種情況。

  • 將一個(gè)函數(shù)注冊(cè)為事件訂閱者
  • 以特定權(quán)限保護(hù)一個(gè)方法
  • 實(shí)現(xiàn)適配器模式

應(yīng)用案例

當(dāng)用于實(shí)現(xiàn)橫切關(guān)注點(diǎn)(cross-cutting concerns)時(shí),修飾器模式會(huì)大顯神威(請(qǐng)參考[Lott14,第223頁]和網(wǎng)頁[t.cn/Rqrl6O0])。以下是橫切關(guān)注點(diǎn)的一些例子。

  • 數(shù)據(jù)校驗(yàn)
  • 事務(wù)處理(這里的事務(wù)類似于數(shù)據(jù)庫事務(wù),意味著要么所有步驟都成功完成,要么事務(wù)失敗) ?緩存
  • 日志
  • 監(jiān)控
  • 調(diào)試
  • 業(yè)務(wù)規(guī)則
  • 壓縮
  • 加密

一般來說,應(yīng)用中有些部件是通用的,可應(yīng)用于其他部件,這樣的部件被看作橫切關(guān)注點(diǎn)。

使用修飾器模式的另一個(gè)常見例子是圖形用戶界面(Graphical User Interface,GUI)工具集。在一個(gè)GUI工具集中,我們希望能夠?qū)⒁恍┨匦裕热邕吙颉㈥幱啊㈩伾约皾L屏,添加到單個(gè)組件/部件。

實(shí)現(xiàn)

Python修飾器通用并且非常強(qiáng)大。你可以在Python官網(wǎng)python.org的修飾器代碼庫頁面(請(qǐng)參考網(wǎng)頁[t.cn/zRHPIq4])中找到許多修飾器的使用樣例。本節(jié)中,我們將學(xué)習(xí)如何實(shí)現(xiàn)一個(gè)memoization修飾器(請(qǐng)參考網(wǎng)頁[t.cn/zQi9AET])。所有遞歸函數(shù)都能因memoization而提速,那么來試試常用的斐波那契數(shù)列例子。使用遞歸算法實(shí)現(xiàn)斐波那契數(shù)列,直接了當(dāng),但性能問題較大,即使對(duì)于很小的數(shù)值也是如此。首先來看看樸素的實(shí)現(xiàn)方法(文件fibonacci_naive.py)。

def fibonacci(n):
    assert(n >= 0), 'n must be >= 0'
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)

if __name__ == '__main__':
    from timeit import Timer
    t = Timer('fibonacci(8)', 'from __main__ import fibonacci')
    print(t.timeit())
15.40320448600687

執(zhí)行一下這個(gè)例子就知道這種實(shí)現(xiàn)的速度有多慢了。計(jì)算第8個(gè)斐波那契數(shù)要花費(fèi)運(yùn)行的樣例輸出如上所示。

使用memoization方法看看能否改善。在下面的代碼中,我們使用一個(gè)dict來緩存斐波那契 數(shù)列中已經(jīng)計(jì)算好的數(shù)值,同時(shí)也修改傳給fabonacci()函數(shù)的參數(shù),計(jì)算第100個(gè)斐波那契數(shù), 而不是第8個(gè)。

known = {0:0, 1:1}
def fibonacci(n):
    assert(n >= 0), 'n must be >= 0'
    if n in known:
        return known[n]
    res = fibonacci(n-1) + fibonacci(n-2)
    known[n] = res
    return res

if __name__ == '__main__':
    from timeit import Timer
    t = Timer('fibonacci(100)', 'from __main__ import fibonacci')
    print(t.timeit())
0.30129148002015427

執(zhí)行基于memoization的代碼實(shí)現(xiàn),可以看到性能得到了極大的提升,甚至對(duì)于計(jì)算大的數(shù) 值性能也是可接受的。運(yùn)行的樣例輸出如上所示。

但這種方法有一些問題。雖然性能不再是一個(gè)問題,但代碼也沒有不使用memoization時(shí)那 樣簡(jiǎn)潔。如果我們決定擴(kuò)展代碼,加入更多的數(shù)學(xué)函數(shù),并將其轉(zhuǎn)變成一個(gè)模塊,那又會(huì)是什么 樣的呢?假設(shè)決定加入的下一個(gè)函數(shù)是nsum(),該函數(shù)返回前n個(gè)數(shù)字的和。注意這個(gè)函數(shù)已存 在于math模塊中,名為fsum(),但我們也能很容易就能想到標(biāo)準(zhǔn)庫中還沒有、但是對(duì)我們模塊 有用的其他函數(shù)(例如,帕斯卡三角形、埃拉托斯特尼篩法等)。所以暫且不必在意示例函數(shù)是 否已存在。使用memoization實(shí)現(xiàn)nsum()函數(shù)的代碼如下所示。

known_sum = {0:0}
def nsum(n):
    assert(n >= 0), 'n must be >= 0'
    if n in known_sum:
        return known_sum[n]
    res = n + nsum(n-1)
    known_sum[n] = res
    return res

你有沒有注意到其中的問題?多了一個(gè)名為known_sum的新字典,為nsum提供緩存作用, 并且函數(shù)本身也比不使用memoization時(shí)的更復(fù)雜。這個(gè)模塊逐步變得不必要地復(fù)雜。保持遞歸 函數(shù)與樸素版本的一樣簡(jiǎn)單,但在性能上又能與使用memoization的函數(shù)相近,這可能嗎?幸運(yùn) 的是,確實(shí)可能,解決方案就是使用修飾器模式。

首先創(chuàng)建一個(gè)如下面的例子所示的memoize()函數(shù)。這個(gè)修飾器接受一個(gè)需要使用 memoization的函數(shù)fn作為輸入,使用一個(gè)名為known的dict作為緩存。函數(shù)functools.wraps() 是一個(gè)為創(chuàng)建修飾器提供便利的函數(shù);雖不強(qiáng)制,但推薦使用,因?yàn)樗鼙A舯恍揎椇瘮?shù)的文檔字符串和簽名(請(qǐng)參考網(wǎng)頁[t.cn/Rqrl0K5])。這種情況要求參數(shù)列表args,因?yàn)楸恍揎椀暮瘮?shù)可能有輸入?yún)?shù)。如果fibonacci()和nsum()不需要任何參數(shù),那么使用args確實(shí)是多余的,但它 們是需要參數(shù)n的。

from functools import wraps
def memoize(fn):
    known = dict()
    @wraps(fn)
    def memoizer(*args):
        if args not in known:
            known[args] = fn(*args)
        return known[args]
    return memoizer

現(xiàn)在,對(duì)樸素版本的函數(shù)應(yīng)用memoize()修飾器。這樣既能保持代碼的可讀性又不影響性能。 我們通過修飾(或修飾行)來應(yīng)用一個(gè)修飾器。修飾使用@name語法,其中name是指我們想要使 用的修飾器的名稱。這其實(shí)只不過是一個(gè)簡(jiǎn)化修飾器使用的語法糖。我們甚至可以繞過這個(gè)語法 手動(dòng)執(zhí)行修飾器,留給你作為練習(xí)吧。來看看下面的例子中如何對(duì)我們的遞歸函數(shù)使用memoize() 修飾器。

@memoize
def nsum(n):
    '''返回前n個(gè)數(shù)字的和'''
    assert(n >= 0), 'n must be <= 0'
    return 0 if n == 0 else n + nsum(n-1)

@memoize
def fibonacci(n):
    '''返回斐波那契數(shù)列的第n個(gè)數(shù)'''
    assert(n >= 0), 'n must be >= 0'
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)

代碼的最后一部分展示如何使用被修飾的函數(shù),并測(cè)量其性能。measure是一個(gè)字典列表,用于避免代碼重復(fù)。注意namedoc分別是如何展示正確的函數(shù)名稱和文檔字符串值的。嘗試從memoize()中刪除@functools.wraps(fn)修飾,看看是否仍舊如此。

if __name__ == '__main__':
    from timeit import Timer
    measure = [ {'exec':'fibonacci(100)', 'import':'fibonacci', 'func':fibonacci},{'exec':'nsum(200)', 'import':'nsum', 'func':nsum} ]
    for m in measure:
            t = Timer('{}'.format(m['exec']), 'from __main__ import {}'.format(m['import']))
            print('name: {}, doc: {}, executing: {}, time: {}'.format(m['func'].__name__, m['func'].__doc__, m['exec'], t.timeit()))
name: fibonacci, doc: 返回斐波那契數(shù)列的第n個(gè)數(shù), executing: fibonacci(100), time: 0.29140055197058246
name: nsum, doc: 返回前n個(gè)數(shù)字的和, executing: nsum(200), time: 0.3004333569551818

看看我們數(shù)學(xué)模塊的完整代碼(文件mymath.py)和執(zhí)行時(shí)的樣例輸出。

from functools import wraps

def memoize(fn):
    known = dict()
    @wraps(fn)
    def memoizer(*args):
        if args not in known:
            known[args] = fn(*args)
        return known[args]
    return memoizer

@memoize
def nsum(n):
    '''返回前n個(gè)數(shù)字的和'''
    assert(n >= 0), 'n must be <= 0'
    return 0 if n == 0 else n + nsum(n-1)

@memoize
def fibonacci(n):
    '''返回斐波那契數(shù)列的第n個(gè)數(shù)'''
    assert(n >= 0), 'n must be >= 0'
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)

if __name__ == '__main__':
    from timeit import Timer
    measure = [ {'exec':'fibonacci(100)', 'import':'fibonacci', 'func':fibonacci},{'exec':'nsum(200)', 'import':'nsum', 'func':nsum} ]
    for m in measure:
            t = Timer('{}'.format(m['exec']), 'from __main__ import {}'.format(m['import']))
            print('name: {}, doc: {}, executing: {}, time: {}'.format(m['func'].__name__, m['func'].__doc__, m['exec'], t.timeit()))
name: fibonacci, doc: 返回斐波那契數(shù)列的第n個(gè)數(shù), executing: fibonacci(100), time: 0.272907609003596
name: nsum, doc: 返回前n個(gè)數(shù)字的和, executing: nsum(200), time: 0.2719842789811082

不錯(cuò)!這一方案同時(shí)具備可讀的代碼和可接受的性能。此時(shí),你可能想爭(zhēng)論說這不是修飾器 模式,因?yàn)槲覀儾⒉皇窃谶\(yùn)行時(shí)應(yīng)用它。被修飾的函數(shù)確實(shí)無法取消修飾,但仍然可以在運(yùn)行時(shí) 決定是否執(zhí)行修飾器。這個(gè)有趣的練習(xí)就留給你來完成吧。

使用修飾器進(jìn)行一層額外的封裝,基于某個(gè)條件來決定是否執(zhí)行真正的修 飾器。

修飾器的另一個(gè)有趣的特性是可以使用多個(gè)修飾器來修飾一個(gè)函數(shù)。本章沒有涉及這一特 性,因此這是另一個(gè)練習(xí),創(chuàng)建一個(gè)修飾器來幫助你調(diào)試遞歸函數(shù),并將其應(yīng)用于nsum()和 fibonacci()。多個(gè)修飾器會(huì)以什么次序執(zhí)行?

如果你仍未充分理解修飾器,那么我有最后一個(gè)練習(xí)留給你。修飾器memoize()無法修飾接 受多個(gè)參數(shù)的函數(shù)。我們?nèi)绾慰梢则?yàn)證這一點(diǎn)?驗(yàn)證之后,嘗試找到一種方法解決這個(gè)問題: 經(jīng)測(cè)試,memoize()對(duì)多參函數(shù)仍然有效。(此處可能有誤)

小結(jié)

本章介紹了修飾器模式及其與Python編程語言的關(guān)聯(lián)。我們使用修飾器模式來擴(kuò)展一個(gè)對(duì)象的行為,無需使用繼承,非常方便。Python進(jìn)一步擴(kuò)展了修飾器的概念,允許我們無需使用繼承或組 合就能擴(kuò)展任意可調(diào)用對(duì)象(函數(shù)、方法或類)的行為。我們可以使用Python內(nèi)置的修飾器特性。

我們看了現(xiàn)實(shí)中一些被修飾對(duì)象的例子,比如槍和照相機(jī)。從軟件的視角來看,Django和Grok都使用了修飾器來達(dá)到不同的目標(biāo),比如控制HTTP壓縮和緩存。
修飾器模式是實(shí)現(xiàn)橫切關(guān)注點(diǎn)的絕佳方案,因?yàn)闄M切關(guān)注點(diǎn)通用但不太適合使用面向?qū)ο缶?程范式來實(shí)現(xiàn)。在5.3節(jié)中我們提到很多種橫切關(guān)注點(diǎn)。事實(shí)上,5.4節(jié)演示了一個(gè)橫切關(guān)注點(diǎn), memoization。我們看到修飾器如何可以幫助我們保持函數(shù)簡(jiǎn)潔,同時(shí)不犧牲性能。

本章中推薦的練習(xí)可以幫助你更好地理解修飾器,這樣你就能將這一強(qiáng)大工具用于解決許多 常見的(或許不太常見的)編程問題。第6章將介紹外觀模式,一種簡(jiǎn)化復(fù)雜系統(tǒng)訪問的方式。


個(gè)人讀后感,好爛的一章,完全就是湊字?jǐn)?shù),還不如干脆挑明了直接解釋傳統(tǒng)意義上的裝飾器模式和python的裝飾器之間的差別,還有自己造了一個(gè)輪子:memorize,其實(shí)我們完全可以使用現(xiàn)有的輪子: from functools import lru_cache,還是別自己造輪子了。。

我后續(xù)會(huì)補(bǔ)充完整這方面的內(nèi)容。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,489評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,510評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,866評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,036評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,585評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,331評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,536評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,754評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,273評(píng)論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,505評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容

  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫完項(xiàng)目接著寫寫一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,606評(píng)論 1 118
  • # Python 資源大全中文版 我想很多程序員應(yīng)該記得 GitHub 上有一個(gè) Awesome - XXX 系列...
    aimaile閱讀 26,535評(píng)論 6 427
  • 葉芝,是一個(gè)農(nóng)村的孩子,從小成績(jī)就名列前茅,也沒有辜負(fù)大家的期望,大學(xué)考上了北京的重點(diǎn)大學(xué)。 高中的時(shí)候,葉芝覺得...
    不要麻醬不要辣椒閱讀 617評(píng)論 0 1
  • 不該去想的,那就舍去。我們都有各自的生活。每個(gè)人的生活都有殘缺,心態(tài)擺正,才能活的自在。
    馮太太閱讀 240評(píng)論 0 0
  • 1.block聲明 返回類型(^block名字)(參數(shù)列表) int(^MyBlock)(int a,int b)...
    crazyfox閱讀 246評(píng)論 0 0