摘要:概括的講,裝飾器的作用就是為已經(jīng)存在的函數(shù)或?qū)ο筇砑宇~外的功能。在理解這些裝飾器之前,最好對(duì)函數(shù)的閉包和裝飾器的接口約定有一定了解。是一個(gè)非常簡(jiǎn)單的裝飾器加強(qiáng)包。
Python中的裝飾器是你進(jìn)入Python大門(mén)的一道坎,不管你跨不跨過(guò)去它都在那里。
為什么需要裝飾器我們假設(shè)你的程序?qū)崿F(xiàn)了say_hello()和say_goodbye()兩個(gè)函數(shù)。
def say_hello(): print "hello!" def say_goodbye(): print "hello!" # bug here if __name__ == "__main__": say_hello() say_goodbye()
但是在實(shí)際調(diào)用中,我們發(fā)現(xiàn)程序出錯(cuò)了,上面的代碼打印了兩個(gè)hello。經(jīng)過(guò)調(diào)試你發(fā)現(xiàn)是say_goodbye()出錯(cuò)了。老板要求調(diào)用每個(gè)方法前都要記錄進(jìn)入函數(shù)的名稱(chēng),比如這樣:
[DEBUG]: Enter say_hello() Hello! [DEBUG]: Enter say_goodbye() Goodbye!
好,小A是個(gè)畢業(yè)生,他是這樣實(shí)現(xiàn)的。
def say_hello(): print "[DEBUG]: enter say_hello()" print "hello!" def say_goodbye(): print "[DEBUG]: enter say_goodbye()" print "hello!" if __name__ == "__main__": say_hello() say_goodbye()
很low吧? 嗯是的。小B工作有一段時(shí)間了,他告訴小A可以這樣寫(xiě)。
def debug(): import inspect caller_name = inspect.stack()[1][3] print "[DEBUG]: enter {}()".format(caller_name) def say_hello(): debug() print "hello!" def say_goodbye(): debug() print "goodbye!" if __name__ == "__main__": say_hello() say_goodbye()
是不是好一點(diǎn)?那當(dāng)然,但是每個(gè)業(yè)務(wù)函數(shù)里都要調(diào)用一下debug()函數(shù),是不是很難受?萬(wàn)一老板說(shuō)say相關(guān)的函數(shù)不用debug,do相關(guān)的才需要呢?
那么裝飾器這時(shí)候應(yīng)該登場(chǎng)了。
裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。它經(jīng)常用于有切面需求的場(chǎng)景,比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場(chǎng)景。裝飾器是解決這類(lèi)問(wèn)題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。
概括的講,裝飾器的作用就是為已經(jīng)存在的函數(shù)或?qū)ο筇砑宇~外的功能。
怎么寫(xiě)一個(gè)裝飾器在早些時(shí)候 (Python Version < 2.4,2004年以前),為一個(gè)函數(shù)添加額外功能的寫(xiě)法是這樣的。
def debug(func): def wrapper(): print "[DEBUG]: enter {}()".format(func.__name__) return func() return wrapper def say_hello(): print "hello!" say_hello = debug(say_hello) # 添加功能并保持原函數(shù)名不變
上面的debug函數(shù)其實(shí)已經(jīng)是一個(gè)裝飾器了,它對(duì)原函數(shù)做了包裝并返回了另外一個(gè)函數(shù),額外添加了一些功能。因?yàn)檫@樣寫(xiě)實(shí)在不太優(yōu)雅,在后面版本的Python中支持了@語(yǔ)法糖,下面代碼等同于早期的寫(xiě)法。
def debug(func): def wrapper(): print "[DEBUG]: enter {}()".format(func.__name__) return func() return wrapper @debug def say_hello(): print "hello!"
這是最簡(jiǎn)單的裝飾器,但是有一個(gè)問(wèn)題,如果被裝飾的函數(shù)需要傳入?yún)?shù),那么這個(gè)裝飾器就壞了。因?yàn)榉祷氐暮瘮?shù)并不能接受參數(shù),你可以指定裝飾器函數(shù)wrapper接受和原函數(shù)一樣的參數(shù),比如:
def debug(func): def wrapper(something): # 指定一毛一樣的參數(shù) print "[DEBUG]: enter {}()".format(func.__name__) return func(something) return wrapper # 返回包裝過(guò)函數(shù) @debug def say(something): print "hello {}!".format(something)
這樣你就解決了一個(gè)問(wèn)題,但又多了N個(gè)問(wèn)題。因?yàn)楹瘮?shù)有千千萬(wàn),你只管你自己的函數(shù),別人的函數(shù)參數(shù)是什么樣子,鬼知道?還好Python提供了可變參數(shù)*args和關(guān)鍵字參數(shù)**kwargs,有了這兩個(gè)參數(shù),裝飾器就可以用于任意目標(biāo)函數(shù)了。
def debug(func): def wrapper(*args, **kwargs): # 指定宇宙無(wú)敵參數(shù) print "[DEBUG]: enter {}()".format(func.__name__) print "Prepare and say...", return func(*args, **kwargs) return wrapper # 返回 @debug def say(something): print "hello {}!".format(something)
至此,你已完全掌握初級(jí)的裝飾器寫(xiě)法。
高級(jí)一點(diǎn)的裝飾器帶參數(shù)的裝飾器和類(lèi)裝飾器屬于進(jìn)階的內(nèi)容。在理解這些裝飾器之前,最好對(duì)函數(shù)的閉包和裝飾器的接口約定有一定了解。(參見(jiàn)http://betacat.online/posts/p...
帶參數(shù)的裝飾器假設(shè)我們前文的裝飾器需要完成的功能不僅僅是能在進(jìn)入某個(gè)函數(shù)后打出log信息,而且還需指定log的級(jí)別,那么裝飾器就會(huì)是這樣的。
def logging(level): def wrapper(func): def inner_wrapper(*args, **kwargs): print "[{level}]: enter function {func}()".format( level=level, func=func.__name__) return func(*args, **kwargs) return inner_wrapper return wrapper @logging(level="INFO") def say(something): print "say {}!".format(something) # 如果沒(méi)有使用@語(yǔ)法,等同于 # say = logging(level="INFO")(say) @logging(level="DEBUG") def do(something): print "do {}...".format(something) if __name__ == "__main__": say("hello") do("my work")
是不是有一些暈?你可以這么理解,當(dāng)帶參數(shù)的裝飾器被打在某個(gè)函數(shù)上時(shí),比如@logging(level="DEBUG"),它其實(shí)是一個(gè)函數(shù),會(huì)馬上被執(zhí)行,只要這個(gè)它返回的結(jié)果是一個(gè)裝飾器時(shí),那就沒(méi)問(wèn)題。細(xì)細(xì)再體會(huì)一下。
基于類(lèi)實(shí)現(xiàn)的裝飾器裝飾器函數(shù)其實(shí)是這樣一個(gè)接口約束,它必須接受一個(gè)callable對(duì)象作為參數(shù),然后返回一個(gè)callable對(duì)象。在Python中一般callable對(duì)象都是函數(shù),但也有例外。只要某個(gè)對(duì)象重載了__call__()方法,那么這個(gè)對(duì)象就是callable的。
class Test(): def __call__(self): print "call me!" t = Test() t() # call me
像__call__這樣前后都帶下劃線的方法在Python中被稱(chēng)為內(nèi)置方法,有時(shí)候也被稱(chēng)為魔法方法。重載這些魔法方法一般會(huì)改變對(duì)象的內(nèi)部行為。上面這個(gè)例子就讓一個(gè)類(lèi)對(duì)象擁有了被調(diào)用的行為。
回到裝飾器上的概念上來(lái),裝飾器要求接受一個(gè)callable對(duì)象,并返回一個(gè)callable對(duì)象(不太嚴(yán)謹(jǐn),詳見(jiàn)后文)。那么用類(lèi)來(lái)實(shí)現(xiàn)也是也可以的。我們可以讓類(lèi)的構(gòu)造函數(shù)__init__()接受一個(gè)函數(shù),然后重載__call__()并返回一個(gè)函數(shù),也可以達(dá)到裝飾器函數(shù)的效果。
class logging(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print "[DEBUG]: enter function {func}()".format( func=self.func.__name__) return self.func(*args, **kwargs) @logging def say(something): print "say {}!".format(something)帶參數(shù)的類(lèi)裝飾器
如果需要通過(guò)類(lèi)形式實(shí)現(xiàn)帶參數(shù)的裝飾器,那么會(huì)比前面的例子稍微復(fù)雜一點(diǎn)。那么在構(gòu)造函數(shù)里接受的就不是一個(gè)函數(shù),而是傳入的參數(shù)。通過(guò)類(lèi)把這些參數(shù)保存起來(lái)。然后在重載__call__方法是就需要接受一個(gè)函數(shù)并返回一個(gè)函數(shù)。
class logging(object): def __init__(self, level="INFO"): self.level = level def __call__(self, func): # 接受函數(shù) def wrapper(*args, **kwargs): print "[{level}]: enter function {func}()".format( level=self.level, func=func.__name__) func(*args, **kwargs) return wrapper #返回函數(shù) @logging(level="INFO") def say(something): print "say {}!".format(something)內(nèi)置的裝飾器
內(nèi)置的裝飾器和普通的裝飾器原理是一樣的,只不過(guò)返回的不是函數(shù),而是類(lèi)對(duì)象,所以更難理解一些。
@property在了解這個(gè)裝飾器前,你需要知道在不使用裝飾器怎么寫(xiě)一個(gè)屬性。
def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x # create a property x = property(getx, setx, delx, "I am doc for x property")
以上就是一個(gè)Python屬性的標(biāo)準(zhǔn)寫(xiě)法,其實(shí)和Java挺像的,但是太羅嗦。有了@語(yǔ)法糖,能達(dá)到一樣的效果但看起來(lái)更簡(jiǎn)單。
@property def x(self): ... # 等同于 def x(self): ... x = property(x)
屬性有三個(gè)裝飾器:setter, getter, deleter ,都是在property()的基礎(chǔ)上做了一些封裝,因?yàn)?b>setter和deleter是property()的第二和第三個(gè)參數(shù),不能直接套用@語(yǔ)法。getter裝飾器和不帶getter的屬性裝飾器效果是一樣的,估計(jì)只是為了湊數(shù),本身沒(méi)有任何存在的意義。經(jīng)過(guò)@property裝飾過(guò)的函數(shù)返回的不再是一個(gè)函數(shù),而是一個(gè)property對(duì)象。
>>> property()@staticmethod,@classmethod
有了@property裝飾器的了解,這兩個(gè)裝飾器的原理是差不多的。@staticmethod返回的是一個(gè)staticmethod類(lèi)對(duì)象,而@classmethod返回的是一個(gè)classmethod類(lèi)對(duì)象。他們都是調(diào)用的是各自的__init__()構(gòu)造函數(shù)。
class classmethod(object): """ classmethod(function) -> method """ def __init__(self, function): # for @classmethod decorator pass # ... class staticmethod(object): """ staticmethod(function) -> method """ def __init__(self, function): # for @staticmethod decorator pass # ...
裝飾器的@語(yǔ)法就等同調(diào)用了這兩個(gè)類(lèi)的構(gòu)造函數(shù)。
class Foo(object): @staticmethod def bar(): pass # 等同于 bar = staticmethod(bar)
至此,我們上文提到的裝飾器接口定義可以更加明確一些,裝飾器必須接受一個(gè)callable對(duì)象,其實(shí)它并不關(guān)心你返回什么,可以是另外一個(gè)callable對(duì)象(大部分情況),也可以是其他類(lèi)對(duì)象,比如property。
裝飾器里的那些坑裝飾器可以讓你代碼更加優(yōu)雅,減少重復(fù),但也不全是優(yōu)點(diǎn),也會(huì)帶來(lái)一些問(wèn)題。
位置錯(cuò)誤的代碼讓我們直接看示例代碼。
def html_tags(tag_name): print "begin outer function." def wrapper_(func): print "begin of inner wrapper function." def wrapper(*args, **kwargs): content = func(*args, **kwargs) print "<{tag}>{content}{tag}>".format(tag=tag_name, content=content) print "end of inner wrapper function." return wrapper print "end of outer function" return wrapper_ @html_tags("b") def hello(name="Toby"): return "Hello {}!".format(name) hello() hello()
在裝飾器中我在各個(gè)可能的位置都加上了print語(yǔ)句,用于記錄被調(diào)用的情況。你知道他們最后打印出來(lái)的順序嗎?如果你心里沒(méi)底,那么最好不要在裝飾器函數(shù)之外添加邏輯功能,否則這個(gè)裝飾器就不受你控制了。以下是輸出結(jié)果:
begin outer function. end of outer function begin of inner wrapper function. end of inner wrapper function. Hello Toby! Hello Toby!錯(cuò)誤的函數(shù)簽名和文檔
裝飾器裝飾過(guò)的函數(shù)看上去名字沒(méi)變,其實(shí)已經(jīng)變了。
def logging(func): def wrapper(*args, **kwargs): """print log before a function.""" print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__) return func(*args, **kwargs) return wrapper @logging def say(something): """say something""" print "say {}!".format(something) print say.__name__ # wrapper
為什么會(huì)這樣呢?只要你想想裝飾器的語(yǔ)法糖@代替的東西就明白了。@等同于這樣的寫(xiě)法。
say = logging(say)
logging其實(shí)返回的函數(shù)名字剛好是wrapper,那么上面的這個(gè)語(yǔ)句剛好就是把這個(gè)結(jié)果賦值給say,say的__name__自然也就是wrapper了,不僅僅是name,其他屬性也都是來(lái)自wrapper,比如doc,source等等。
使用標(biāo)準(zhǔn)庫(kù)里的functools.wraps,可以基本解決這個(gè)問(wèn)題。
from functools import wraps def logging(func): @wraps(func) def wrapper(*args, **kwargs): """print log before a function.""" print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__) return func(*args, **kwargs) return wrapper @logging def say(something): """say something""" print "say {}!".format(something) print say.__name__ # say print say.__doc__ # say something
看上去不錯(cuò)!主要問(wèn)題解決了,但其實(shí)還不太完美。因?yàn)楹瘮?shù)的簽名和源碼還是拿不到的。
import inspect print inspect.getargspec(say) # failed print inspect.getsource(say) # failed
如果要徹底解決這個(gè)問(wèn)題可以借用第三方包,比如wrapt。后文有介紹。
不能裝飾@staticmethod 或者 @classmethod當(dāng)你想把裝飾器用在一個(gè)靜態(tài)方法或者類(lèi)方法時(shí),不好意思,報(bào)錯(cuò)了。
class Car(object): def __init__(self, model): self.model = model @logging # 裝飾實(shí)例方法,OK def run(self): print "{} is running!".format(self.model) @logging # 裝飾靜態(tài)方法,F(xiàn)ailed @staticmethod def check_model_for(obj): if isinstance(obj, Car): print "The model of your car is {}".format(obj.model) else: print "{} is not a car!".format(obj) """ Traceback (most recent call last): ... File "example_4.py", line 10, in logging @wraps(func) File "C:Python27libfunctools.py", line 33, in update_wrapper setattr(wrapper, attr, getattr(wrapped, attr)) AttributeError: "staticmethod" object has no attribute "__module__" """
前面已經(jīng)解釋了@staticmethod這個(gè)裝飾器,其實(shí)它返回的并不是一個(gè)callable對(duì)象,而是一個(gè)staticmethod對(duì)象,那么它是不符合裝飾器要求的(比如傳入一個(gè)callable對(duì)象),你自然不能在它之上再加別的裝飾器。要解決這個(gè)問(wèn)題很簡(jiǎn)單,只要把你的裝飾器放在@staticmethod之前就好了,因?yàn)槟愕难b飾器返回的還是一個(gè)正常的函數(shù),然后再加上一個(gè)@staticmethod是不會(huì)出問(wèn)題的。
class Car(object): def __init__(self, model): self.model = model @staticmethod @logging # 在@staticmethod之前裝飾,OK def check_model_for(obj): pass如何優(yōu)化你的裝飾器
嵌套的裝飾函數(shù)不太直觀,我們可以使用第三方包類(lèi)改進(jìn)這樣的情況,讓裝飾器函數(shù)可讀性更好。
decorator.pydecorator.py 是一個(gè)非常簡(jiǎn)單的裝飾器加強(qiáng)包。你可以很直觀的先定義包裝函數(shù)wrapper(),再使用decorate(func, wrapper)方法就可以完成一個(gè)裝飾器。
from decorator import decorate def wrapper(func, *args, **kwargs): """print log before a function.""" print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__) return func(*args, **kwargs) def logging(func): return decorate(func, wrapper) # 用wrapper裝飾func
你也可以使用它自帶的@decorator裝飾器來(lái)完成你的裝飾器。
from decorator import decorator @decorator def logging(func, *args, **kwargs): print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__) return func(*args, **kwargs)
decorator.py實(shí)現(xiàn)的裝飾器能完整保留原函數(shù)的name,doc和args,唯一有問(wèn)題的就是inspect.getsource(func)返回的還是裝飾器的源代碼,你需要改成inspect.getsource(func.__wrapped__)。
wraptwrapt是一個(gè)功能非常完善的包,用于實(shí)現(xiàn)各種你想到或者你沒(méi)想到的裝飾器。使用wrapt實(shí)現(xiàn)的裝飾器你不需要擔(dān)心之前inspect中遇到的所有問(wèn)題,因?yàn)樗紟湍闾幚砹耍踔?b>inspect.getsource(func)也準(zhǔn)確無(wú)誤。
import wrapt # without argument in decorator @wrapt.decorator def logging(wrapped, instance, args, kwargs): # instance is must print "[DEBUG]: enter {}()".format(wrapped.__name__) return wrapped(*args, **kwargs) @logging def say(something): pass
使用wrapt你只需要定義一個(gè)裝飾器函數(shù),但是函數(shù)簽名是固定的,必須是(wrapped, instance, args, kwargs),注意第二個(gè)參數(shù)instance是必須的,就算你不用它。當(dāng)裝飾器裝飾在不同位置時(shí)它將得到不同的值,比如裝飾在類(lèi)實(shí)例方法時(shí)你可以拿到這個(gè)類(lèi)實(shí)例。根據(jù)instance的值你能夠更加靈活的調(diào)整你的裝飾器。另外,args和kwargs也是固定的,注意前面沒(méi)有星號(hào)。在裝飾器內(nèi)部調(diào)用原函數(shù)時(shí)才帶星號(hào)。
如果你需要使用wrapt寫(xiě)一個(gè)帶參數(shù)的裝飾器,可以這樣寫(xiě)。
def logging(level): @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): print "[{}]: enter {}()".format(level, wrapped.__name__) return wrapped(*args, **kwargs) return wrapper @logging(level="INFO") def do(work): pass
關(guān)于wrapt的使用,建議查閱官方文檔,在此不在贅述。
http://wrapt.readthedocs.io/e...
小結(jié)Python的裝飾器和Java的注解(Annotation)并不是同一回事,和C#中的特性(Attribute)也不一樣,完全是兩個(gè)概念。
裝飾器的理念是對(duì)原函數(shù)、對(duì)象的加強(qiáng),相當(dāng)于重新封裝,所以一般裝飾器函數(shù)都被命名為wrapper(),意義在于包裝。函數(shù)只有在被調(diào)用時(shí)才會(huì)發(fā)揮其作用。比如@logging裝飾器可以在函數(shù)執(zhí)行時(shí)額外輸出日志,@cache裝飾過(guò)的函數(shù)可以緩存計(jì)算結(jié)果等等。
而注解和特性則是對(duì)目標(biāo)函數(shù)或?qū)ο筇砑右恍傩裕喈?dāng)于將其分類(lèi)。這些屬性可以通過(guò)反射拿到,在程序運(yùn)行時(shí)對(duì)不同的特性函數(shù)或?qū)ο蠹右愿深A(yù)。比如帶有Setup的函數(shù)就當(dāng)成準(zhǔn)備步驟執(zhí)行,或者找到所有帶有TestMethod的函數(shù)依次執(zhí)行等等。
至此我所了解的裝飾器已經(jīng)講完,但是還有一些內(nèi)容沒(méi)有提到,比如裝飾類(lèi)的裝飾器。有機(jī)會(huì)再補(bǔ)充。謝謝觀看。
本文源碼 https://github.com/tobyqin/py...
關(guān)于作者:Python技術(shù)愛(ài)好者,目前從事測(cè)試開(kāi)發(fā)相關(guān)工作,轉(zhuǎn)載請(qǐng)注明原文出處。
歡迎關(guān)注我的博客 http://betacat.online,你可以到我的公眾號(hào)中去當(dāng)吃瓜群眾。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/44230.html
摘要:為了避免重復(fù)調(diào)用,可以適當(dāng)?shù)刈鼍彺?,的裝飾器可以完美的完成這一任務(wù)。這意味著我們可以為方法創(chuàng)建裝飾器,只是要記得考慮。裝飾器封裝了函數(shù),這使得調(diào)試函數(shù)變得困難。另外,使用裝飾器去管理緩存和權(quán)限。 原文地址 之前用python簡(jiǎn)單寫(xiě)了一下斐波那契數(shù)列的遞歸實(shí)現(xiàn)(如下),發(fā)現(xiàn)運(yùn)行速度很慢。 def fib_direct(n): assert n > 0, invalid n ...
摘要:今天我們一起探討一下裝飾器的另類(lèi)用法。語(yǔ)法回顧開(kāi)始之前我們?cè)賹⒀b飾器的語(yǔ)法回顧一下。例子本身只是演示了裝飾器的一種用法,但不是推薦你就這樣使用裝飾器。類(lèi)裝飾器在以前,還不支持類(lèi)裝飾器。 之前有比較系統(tǒng)介紹過(guò)Python的裝飾器(請(qǐng)查閱《詳解Python裝飾器》),本文算是一個(gè)補(bǔ)充。今天我們一起探討一下裝飾器的另類(lèi)用法。 語(yǔ)法回顧 開(kāi)始之前我們?cè)賹ython裝飾器的語(yǔ)法回顧一下。 @d...
小編寫(xiě)這篇文章的主要目的,主要是來(lái)給大家介紹,關(guān)于python中,相關(guān)語(yǔ)法問(wèn)題的解答,比如在python,我們會(huì)遇到閉包和裝飾器不會(huì)用的情況,那么,下文就會(huì)來(lái)給大家做一個(gè)詳細(xì)的解答?! ?args與**kwarsg及閉包和裝飾器 過(guò)程 先理解閉包,再理解裝飾器,不要忘了不定長(zhǎng)參數(shù) deffunc(): msg='111' deffunc1(): print(ms...
摘要:在學(xué)習(xí)裝飾器語(yǔ)法之前,需要先溫習(xí)一下的一些基礎(chǔ)知識(shí)。函數(shù)最后必須返回。使用時(shí)也很簡(jiǎn)單,如下在方法前面加上,就是裝飾器語(yǔ)法。裝備了,攻擊更強(qiáng)了。職業(yè)的基本攻擊穿上了,移動(dòng)速度更快了。 在學(xué)習(xí)ES7裝飾器語(yǔ)法之前,需要先溫習(xí)一下ES5的一些基礎(chǔ)知識(shí)。 假設(shè)有對(duì)象如下:(便于理解) var person = { name: TOM } 在ES5中,對(duì)象中的每個(gè)屬性都有一個(gè)特性值來(lái)描述...
摘要:最近看前端都展開(kāi)了幾場(chǎng)而我大知乎最熱語(yǔ)言還沒(méi)有相關(guān)。有關(guān)書(shū)籍的介紹,大部分截取自是官方介紹。但從開(kāi)始,標(biāo)準(zhǔn)庫(kù)為我們提供了模塊,它提供了和兩個(gè)類(lèi),實(shí)現(xiàn)了對(duì)和的進(jìn)一步抽象,對(duì)編寫(xiě)線程池進(jìn)程池提供了直接的支持。 《流暢的python》閱讀筆記 《流暢的python》是一本適合python進(jìn)階的書(shū), 里面介紹的基本都是高級(jí)的python用法. 對(duì)于初學(xué)python的人來(lái)說(shuō), 基礎(chǔ)大概也就夠用了...
閱讀 3356·2021-11-23 09:51
閱讀 1141·2021-09-03 10:30
閱讀 3273·2021-08-31 09:40
閱讀 3336·2019-08-30 14:22
閱讀 977·2019-08-30 14:09
閱讀 2964·2019-08-30 13:21
閱讀 3325·2019-08-28 18:03
閱讀 2914·2019-08-26 13:44