Python?面向切面編程?AOP?及裝飾器
什么是 AOP
AOP,就是面向切面編程,簡(jiǎn)單的說(shuō),就是動(dòng)態(tài)地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程。我們管切入到指定類指定方法的代碼片段稱為切面,而切入到哪些類、哪些方法則叫切入點(diǎn)。這樣我們就可以把幾個(gè)類共有的代碼,抽取到一個(gè)切片中,等到需要時(shí)再切入對(duì)象中去,從而改變其原有的行為。這種思想,可以使原有代碼邏輯更清晰,對(duì)原有代碼毫無(wú)入侵性,常用于像權(quán)限管理,日志記錄,事物管理等等。而 Python 中的裝飾器就是很著名的設(shè)計(jì),常用于有切面需求的場(chǎng)景。類如,Django 中就大量使用裝飾器去完成一下切面需求,如權(quán)限控制,內(nèi)容過(guò)濾,請(qǐng)求管理等等。
裝飾器
Python 裝飾器(fuctional decorators)就是用于拓展原來(lái)函數(shù)功能的一種函數(shù),目的是在不改變?cè)瘮?shù)名或類名的情況下,給函數(shù)增加新的功能。
下面就跟我一起詳細(xì)的了解下裝飾器是如何工作的。首先,要明確一個(gè)概念:Python 中萬(wàn)物皆對(duì)象,函數(shù)也是是對(duì)象!所以,一個(gè)函數(shù)作為對(duì)象,可以在另一個(gè)函數(shù)中定義。
看下面示例:
def?a(): ????def?b(): ????????print("I'm?b") ????b() ????c?=?b ????return?c d?=?a() d() b() c()
輸出結(jié)果為:
I'm b
I'm b
拋出 NameError: name 'b' is not defined 錯(cuò)誤
拋出 NameError: name 'c' is not defined 錯(cuò)誤
從上可以看出,由于函數(shù)是對(duì)象,所以:
- 可以賦值給變量
- 可以在另一個(gè)函數(shù)中定義
然后,return
可以返回一個(gè)函數(shù)對(duì)象。這個(gè)函數(shù)對(duì)象是在另一個(gè)函數(shù)中定義的。由于作用域不同,所以只有 return
返回的函數(shù)可以調(diào)用,在函數(shù) a
中定義和賦值的函數(shù) b
和 c
在外部作用域是無(wú)法調(diào)用的。
這意味著一個(gè)功能可以 return
另一個(gè)功能。
除了可以作為對(duì)象返回外,函數(shù)對(duì)象還可以作為參數(shù)傳遞給另一個(gè)函數(shù):
def?a(): ????print("I'm?a") def?b(func): ????print("I'm?b") ????func() b(a)
輸出結(jié)果:
I'm b
I'm a
OK,現(xiàn)在,基于函數(shù)的這些特性,我們就可以創(chuàng)建一個(gè)裝飾器,用來(lái)在不改變?cè)瘮?shù)的情況下,實(shí)現(xiàn)功能。
函數(shù)裝飾器
比如,我們要在函數(shù)執(zhí)行前和執(zhí)行后分別執(zhí)行一些別的操作,那么根據(jù)上面函數(shù)可以作為參數(shù)傳遞,我們可以這樣實(shí)現(xiàn),看下面示例:
def?a(): ????print("I'm?a") def?b(func): ????print('在函數(shù)執(zhí)行前,做一些操作') ????func() ????print("在函數(shù)執(zhí)行后,做一些操作") b(a)
輸出結(jié)果:
在函數(shù)執(zhí)行前,做一些操作
I'm a
在函數(shù)執(zhí)行后,做一些操作
但是這樣的話,原函數(shù)就變成了另一個(gè)函數(shù),每加一個(gè)功能,就要在外面包一層新的函數(shù),這樣原來(lái)調(diào)用的地方,就會(huì)需要全部修改,這明顯不方便,就會(huì)想,有沒(méi)有辦法可以讓函數(shù)的操作改變,但是名稱不改變,還是作為原函數(shù)呢。
看下面示例:
def?a(): ????print("I'm?a") def?c(func): ????def?b(): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????func() ????????print("在函數(shù)執(zhí)行后,做一些操作") ????return?b a?=?c(a) a()
輸出結(jié)果:
在函數(shù)執(zhí)行前,做一些操作
I'm a
在函數(shù)執(zhí)行后,做一些操作
如上,我們可以將函數(shù)再包一層,將新的函數(shù) b
,作為對(duì)象返回。這樣通過(guò)函數(shù) c
,將 a
改變并重新賦值給 a
,這樣就實(shí)現(xiàn)了改變函數(shù) a
,并同樣使用函數(shù) a
來(lái)調(diào)用。
但是這樣寫起來(lái)非常不方便,因?yàn)樾枰匦沦x值,所以在 Python 中,可以通過(guò) @
來(lái)實(shí)現(xiàn),將函數(shù)作為參數(shù)傳遞。
看下示例:
def?c(func): ????def?b(): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????func() ????????print("在函數(shù)執(zhí)行后,做一些操作") ????return?b @c def?a(): ????print("I'm?a") a()
輸出結(jié)果:
在函數(shù)執(zhí)行前,做一些操作
I'm a
在函數(shù)執(zhí)行后,做一些操作
如上,通過(guò) @c
,就實(shí)現(xiàn)了將函數(shù) a
作為參數(shù),傳入 c
,并將返回的函數(shù)重新作為函數(shù) a
。這 c
也就是一個(gè)簡(jiǎn)單的函數(shù)裝飾器。
且如果函數(shù)是有返回值的,那么改變后的函數(shù)也需要有返回值,如下所以:
def?c(func): ????def?b(): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????result?=?func() ????????print("在函數(shù)執(zhí)行后,做一些操作") ????????return?result ????return?b @c def?a(): ????print("函數(shù)執(zhí)行中。。。") ????return?"I'm?a" print(a())
輸出結(jié)果:
在函數(shù)執(zhí)行前,做一些操作
函數(shù)執(zhí)行中。。。
在函數(shù)執(zhí)行后,做一些操作
I'm a
如上所示:通過(guò)將返回值進(jìn)行傳遞,就可以實(shí)現(xiàn)函數(shù)執(zhí)行前后的操作。但是你會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,就是為什么輸出 I'm a
會(huì)在最后才打印出來(lái)?
因?yàn)?nbsp;I'm a
是返回的結(jié)果,而實(shí)際上函數(shù)是 print("在函數(shù)執(zhí)行后,做一些操作")
這一操作前運(yùn)行的,只是先將返回的結(jié)果給到了 result
,然后 result
傳遞出來(lái),最后由最下方的 print(a())
打印了出來(lái)。
那如何函數(shù) a
帶參數(shù)怎么辦呢?很簡(jiǎn)單,函數(shù) a
帶參數(shù),那么我們返回的函數(shù)也同樣要帶參數(shù)就好啦。
看下面示例:
def?c(func): ????def?b(name,?age): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????result?=?func(name,?age) ????????print("在函數(shù)執(zhí)行后,做一些操作") ????????return?result ????return?b @c def?a(name,?age): ????print("函數(shù)執(zhí)行中。。。") ????return?"我是?{},?今年{}歲?".format(name,?age) print(a('Amos',?24))
輸出結(jié)果:
在函數(shù)執(zhí)行前,做一些操作
函數(shù)執(zhí)行中。。。
在函數(shù)執(zhí)行后,做一些操作
我是 Amos, 今年24歲
但是又有問(wèn)題了,我寫一個(gè)裝飾器 c
,需要裝飾多個(gè)不同的函數(shù),這些函數(shù)的參數(shù)各不相同,那么怎么辦呢?簡(jiǎn)單,用 *args
和 **kwargs
來(lái)表示所有參數(shù)即可。
如下示例:
def?c(func): ????def?b(*args,?**kwargs): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????result?=?func(*args,?**kwargs) ????????print("在函數(shù)執(zhí)行后,做一些操作") ????????return?result ????return?b @c def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) @c def?d(sex,?height): ????print('函數(shù)執(zhí)行中。。。') ????return?'性別:{},身高:{}'.format(sex,?height) print(a('Amos',?24)) print(d('男',?175))
輸出結(jié)果:
在函數(shù)執(zhí)行前,做一些操作
函數(shù)執(zhí)行中。。。
在函數(shù)執(zhí)行后,做一些操作
我是 Amos, 今年24歲在函數(shù)執(zhí)行前,做一些操作
函數(shù)執(zhí)行中。。。
在函數(shù)執(zhí)行后,做一些操作
性別:男,身高:175
如上就解決了參數(shù)的問(wèn)題,哇,這么好用。那是不是這樣就沒(méi)有問(wèn)題了?并不是!經(jīng)過(guò)裝飾器裝飾后的函數(shù),實(shí)際上已經(jīng)變成了裝飾器函數(shù) c
中定義的函數(shù) b
,所以函數(shù)的元數(shù)據(jù)則全部改變了!
如下示例:
def?c(func): ????def?b(*args,?**kwargs): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????result?=?func(*args,?**kwargs) ????????print("在函數(shù)執(zhí)行后,做一些操作") ????????return?result ????return?b @c def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(a.__name__)
輸出結(jié)果:
b
會(huì)發(fā)現(xiàn)函數(shù)實(shí)際上是函數(shù) b
了,這就有問(wèn)題了,那么該怎么解決呢,有人就會(huì)想到,可以在裝飾器函數(shù)中先把原函數(shù)的元數(shù)據(jù)保存下來(lái),在最后再講 b
函數(shù)的元數(shù)據(jù)改為原函數(shù)的,再返回 b
。這樣的確是可以的!但我們不這樣用,為什么?
因?yàn)?Python 早就想到這個(gè)問(wèn)題啦,所以給我們提供了一個(gè)內(nèi)置的方法,來(lái)自動(dòng)實(shí)現(xiàn)原數(shù)據(jù)的保存和替換工作。哈哈,這樣就不同我們自己動(dòng)手啦!
看下面示例:
from?functools?import?wraps def?c(func): ????@wraps(func) ????def?b(*args,?**kwargs): ????????print('在函數(shù)執(zhí)行前,做一些操作') ????????result?=?func(*args,?**kwargs) ????????print("在函數(shù)執(zhí)行后,做一些操作") ????????return?result ????return?b @c def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(a.__name__)
輸出結(jié)果:
a
使用內(nèi)置的 wraps
裝飾器,將原函數(shù)作為裝飾器參數(shù),實(shí)現(xiàn)函數(shù)原數(shù)據(jù)的保留替換功能。
耶!裝飾器還可以帶參數(shù)啊,你看上面 wraps
裝飾器就傳入了參數(shù)。哈哈,是的,裝飾器還可以帶參數(shù),那怎么實(shí)現(xiàn)呢?
看下面示例:
from?functools?import?wraps def?d(name): ????def?c(func): ????????@wraps(func) ????????def?b(*args,?**kwargs): ????????????print('裝飾器傳入?yún)?shù)為:{}'.format(name)) ????????????print('在函數(shù)執(zhí)行前,做一些操作') ????????????result?=?func(*args,?**kwargs) ????????????print("在函數(shù)執(zhí)行后,做一些操作") ????????????return?result ????????return?b ????return?c @d(name='我是裝飾器參數(shù)') def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(a('Amos',?24))
輸出結(jié)果:
裝飾器傳入?yún)?shù)為:我是裝飾器參數(shù)
在函數(shù)執(zhí)行前,做一些操作
函數(shù)執(zhí)行中。。。
在函數(shù)執(zhí)行后,做一些操作
我是 Amos, 今年24歲
如上所示,很簡(jiǎn)單,只需要在原本的裝飾器之上,再包一層,相當(dāng)于先接收裝飾器參數(shù),然后返回一個(gè)不帶參數(shù)的裝飾器,然后再將函數(shù)傳入,最后返回變化后的函數(shù)。
這樣就可以實(shí)現(xiàn)很多功能了,這樣可以根據(jù)傳給裝飾器的參數(shù)不同,來(lái)分別實(shí)現(xiàn)不同的功能。
另外,可能會(huì)有人問(wèn), 可以在同一個(gè)函數(shù)上,使用多個(gè)裝飾器嗎?答案是:可以!
看下面示例:
from?functools?import?wraps def?d(name): ????def?c(func): ????????@wraps(func) ????????def?b(*args,?**kwargs): ????????????print('裝飾器傳入?yún)?shù)為:{}'.format(name)) ????????????print('我是裝飾器d:?在函數(shù)執(zhí)行前,做一些操作') ????????????result?=?func(*args,?**kwargs) ????????????print("我是裝飾器d:?在函數(shù)執(zhí)行后,做一些操作") ????????????return?result ????????return?b ????return?c def?e(name): ????def?c(func): ????????@wraps(func) ????????def?b(*args,?**kwargs): ????????????print('裝飾器傳入?yún)?shù)為:{}'.format(name)) ????????????print('我是裝飾器e:?在函數(shù)執(zhí)行前,做一些操作') ????????????result?=?func(*args,?**kwargs) ????????????print("我是裝飾器e:?在函數(shù)執(zhí)行后,做一些操作") ????????????return?result ????????return?b ????return?c @e(name='我是裝飾器e') @d(name='我是裝飾器d') def?func_a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(func_a('Amos',?24)) 行后,做一些操作 我是?Amos,?今年24歲?
輸出結(jié)果:
裝飾器傳入?yún)?shù)為:我是裝飾器e
我是裝飾器e: 在函數(shù)執(zhí)行前,做一些操作裝飾器傳入?yún)?shù)為:我是裝飾器d
我是裝飾器d: 在函數(shù)執(zhí)行前,做一些操作
函數(shù)執(zhí)行中。。。
我是裝飾器d: 在函數(shù)執(zhí)行后,做一些操作我是裝飾器e: 在函數(shù)執(zhí)
如上所示,當(dāng)兩個(gè)裝飾器同時(shí)使用時(shí),可以想象成洋蔥,最下層的裝飾器先包裝一層,然后一直到最上層裝飾器,完成多層的包裝。然后執(zhí)行時(shí),就像切洋蔥,從最外層開(kāi)始,只執(zhí)行到被裝飾函數(shù)運(yùn)行時(shí),就到了下一層,下一層又執(zhí)行到函數(shù)運(yùn)行時(shí)到下一層,一直到執(zhí)行了被裝飾函數(shù)后,就像切到了洋蔥的中間,然后再往下,依次從最內(nèi)層開(kāi)始,依次執(zhí)行到最外層。
示例:
當(dāng)一個(gè)函數(shù) a
被 b
,c
,d
三個(gè)裝飾器裝飾時(shí),執(zhí)行順序如下圖所示,多個(gè)同理。
@d @c @b def?a(): ????pass
類裝飾器
在函數(shù)裝飾器方面,很多人搞不清楚,是因?yàn)檠b飾器可以用函數(shù)實(shí)現(xiàn)(像上面),也可以用類實(shí)現(xiàn)。因?yàn)楹瘮?shù)和類都是對(duì)象,同樣可以作為被裝飾的對(duì)象,所以根據(jù)被裝飾的對(duì)象不同,一同有下面四種情況:
- 函數(shù)裝飾函數(shù)
- 類裝飾函數(shù)
- 函數(shù)裝飾類
- 類裝飾類
下面我們依次來(lái)說(shuō)明一下這四種情況的使用。
1、函數(shù)裝飾函數(shù)
from?functools?import?wraps def?d(name): ????def?c(func): ????????@wraps(func) ????????def?b(*args,?**kwargs): ????????????print('裝飾器傳入?yún)?shù)為:{}'.format(name)) ????????????print('在函數(shù)執(zhí)行前,做一些操作') ????????????result?=?func(*args,?**kwargs) ????????????print("在函數(shù)執(zhí)行后,做一些操作") ????????????return?result ????????return?b ????return?c @d(name='我是裝飾器參數(shù)') def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(a.__class__) #?輸出結(jié)果: <class?'function'>
此為最常見(jiàn)的裝飾器,用于裝飾函數(shù),返回的是一個(gè)函數(shù)。
2、類裝飾函數(shù)
也就是通過(guò)類來(lái)實(shí)現(xiàn)裝飾器的功能而已。通過(guò)類的 __call__
方法實(shí)現(xiàn):
from?functools?import?wraps class?D(object): ????def?__init__(self,?name): ????????self._name?=?name ????def?__call__(self,?func): ????????@wraps(func) ????????def?wrapper(*args,?**kwargs): ????????????print('裝飾器傳入?yún)?shù)為:{}'.format(self._name)) ????????????print('在函數(shù)執(zhí)行前,做一些操作') ????????????result?=?func(*args,?**kwargs) ????????????print("在函數(shù)執(zhí)行后,做一些操作") ????????????return?result ????????return?wrapper @D(name='我是裝飾器參數(shù)') def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(a.__class__) #?輸出結(jié)果: <class?'function'>
以上所示,只是將用函數(shù)定義的裝飾器改為使用類來(lái)實(shí)現(xiàn)而已。還是用于裝飾函數(shù),因?yàn)樵陬惖?nbsp;__call__
中,最后返回的還是一個(gè)函數(shù)。
此為帶裝飾器參數(shù)的裝飾器實(shí)現(xiàn)方法,是通過(guò) __call__
方法。
若裝飾器不帶參數(shù),則可以將 __init__
方法去掉,但是在使用裝飾器時(shí),需要 @D()
這樣使用,如下:
from?functools?import?wraps class?D(object): ????def?__call__(self,?func): ????????@wraps(func) ????????def?wrapper(*args,?**kwargs): ????????????print('在函數(shù)執(zhí)行前,做一些操作') ????????????result?=?func(*args,?**kwargs) ????????????print("在函數(shù)執(zhí)行后,做一些操作") ????????????return?result ????????return?wrapper @D() def?a(name,?age): ????print('函數(shù)執(zhí)行中。。。') ????return?"我是?{},?今年{}歲?".format(name,?age) print(a.__class__) #?輸出結(jié)果: <class?'function'>
如上是比較方便簡(jiǎn)答的,使用類定義函數(shù)裝飾器,且返回對(duì)象為函數(shù),元數(shù)據(jù)保留。
3、函數(shù)裝飾類
下面重點(diǎn)來(lái)啦,我們常見(jiàn)的裝飾器都是用于裝飾函數(shù)的,返回的對(duì)象也是一個(gè)函數(shù),而要裝飾類,那么返回的對(duì)象就要是類,且類的元數(shù)據(jù)等也要保留。
不怕丟臉的說(shuō),目前我還不知道怎么實(shí)現(xiàn)完美的類裝飾器,在裝飾類的時(shí)候,一般有兩種方法:
返回一個(gè)函數(shù),實(shí)現(xiàn)類在創(chuàng)建實(shí)例的前后執(zhí)行操作,并正常返回此類的實(shí)例。但是這樣經(jīng)過(guò)裝飾器的類就屬于函數(shù)了,其無(wú)法繼承,但可以正常調(diào)用創(chuàng)建實(shí)例。
如下:
from?functools?import?wraps def?d(name): ????def?c(cls): ????????@wraps(cls) ????????def?b(*args,?**kwargs): ????????????print('裝飾器傳入?yún)?shù)為:{}'.format(name)) ????????????print('在類初始化前,做一些操作') ????????????instance?=?cls(*args,?**kwargs) ????????????print("在類初始化后,做一些操作") ????????????return?instance ????????return?b ????return?c @d(name='我是裝飾器參數(shù)') class?A(object): ????def?__init__(self,?name,?age): ????????self.name?=?name ????????self.age?=?age ????????print('類初始化實(shí)例,{}?{}'.format(self.name,?self.age)) a?=?A('Amos',?24) print(a.__class__) print(A.__class__) #?輸出結(jié)果: 裝飾器傳入?yún)?shù)為:我是裝飾器參數(shù) 在類初始化前,做一些操作 類初始化實(shí)例,Amos?24 在類初始化后,做一些操作 <class?'__main__.A'> <class?'function'>
如上所示,就是第一種方法。
4、類裝飾類
接上文,返回一個(gè)類,實(shí)現(xiàn)類在創(chuàng)建實(shí)例的前后執(zhí)行操作,但類已經(jīng)改變了,創(chuàng)建的實(shí)例也已經(jīng)不是原本類的實(shí)例了。
看下面示例:
def?desc(name): ????def?decorator(aClass): ????????class?Wrapper(object): ????????????def?__init__(self,?*args,?**kwargs): ????????????????print('裝飾器傳入?yún)?shù)為:{}'.format(name)) ????????????????print('在類初始化前,做一些操作') ????????????????self.wrapped?=?aClass(*args,?**kwargs) ????????????????print("在類初始化后,做一些操作") ????????????def?__getattr__(self,?name): ????????????????print('Getting?the?{}?of?{}'.format(name,?self.wrapped)) ????????????????return?getattr(self.wrapped,?name) ????????????def?__setattr__(self,?key,?value): ????????????????if?key?==?'wrapped':??#?這里捕捉對(duì)wrapped的賦值 ????????????????????self.__dict__[key]?=?value ????????????????else: ????????????????????setattr(self.wrapped,?key,?value) ????????return?Wrapper ????return?decorator @desc(name='我是裝飾器參數(shù)') class?A(object): ????def?__init__(self,?name,?age): ????????self.name?=?name ????????self.age?=?age ????????print('類初始化實(shí)例,{}?{}'.format(self.name,?self.age)) a?=?A('Amos',?24) print(a.__class__) print(A.__class__) print(A.__name__)
輸出結(jié)果:
裝飾器傳入?yún)?shù)為:我是裝飾器參數(shù)
在類初始化前,做一些操作
類初始化實(shí)例,Amos 24
在類初始化后,做一些操作
<class '__main__.desc.<locals>.decorator.<locals>.Wrapper'>
<class 'type'>
Wrapper
如上,看到了嗎,通過(guò)在函數(shù)中新定義類,并返回類,這樣函數(shù)還是類,但是經(jīng)過(guò)裝飾器后,類 A
已經(jīng)變成了類 Wrapper
,且生成的實(shí)例 a
也是類 Wrapper
的實(shí)例,即使通過(guò) __getattr__
和 __setattr__
兩個(gè)方法,使得實(shí)例a的屬性都是在由類 A
創(chuàng)建的實(shí)例 wrapped
的屬性,但是類的元數(shù)據(jù)無(wú)法改變。很多內(nèi)置的方法也就會(huì)有問(wèn)題。我個(gè)人是不推薦這種做法的!
所以,我推薦在代碼中,盡量避免類裝飾器的使用,如果要在類中做一些操作,完全可以通過(guò)修改類的魔法方法,繼承,元類等等方式來(lái)實(shí)現(xiàn)。如果避免不了,那也請(qǐng)謹(jǐn)慎處理。
到此這篇關(guān)于Python 面向切面編程 AOP 及裝飾器的文章就介紹到這了,更多相關(guān)Python AOP 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決keras GAN訓(xùn)練是loss不發(fā)生變化,accuracy一直為0.5的問(wèn)題
這篇文章主要介紹了解決keras GAN訓(xùn)練是loss不發(fā)生變化,accuracy一直為0.5的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07python進(jìn)程管理工具supervisor安裝使用
supervisor是一個(gè)用python語(yǔ)言編寫的進(jìn)程管理工具,它可以很方便的監(jiān)聽(tīng)、啟動(dòng)、停止、重啟一個(gè)或多個(gè)進(jìn)程,本文給大家介紹python進(jìn)程管理工具supervisor安裝使用配置教程,感興趣的朋友一起看看吧2023-08-08Python插件virtualenv搭建虛擬環(huán)境
這篇文章主要為大家詳細(xì)介紹了Python插件virtualenv搭建虛擬環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11