欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中裝飾器的基本功能理解

 更新時間:2021年10月22日 09:33:38   作者:曉鵬-King  
裝飾器本質(zhì)上是一個Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外功能,下面這篇文章主要給大家介紹了關(guān)于Python中裝飾器的基本功能,需要的朋友可以參考下

前言

在 python 中,裝飾器由于是 python 語言自帶的一個功能,因此,對于其實(shí)現(xiàn)以及其用法就會感到比較奇怪,這里我記錄一下對它的理解,加深自己的印象。

什么是裝飾器

對于什么是裝飾器,我們其實(shí)應(yīng)該知道為什么會存在裝飾器。

​ 裝飾器是 python 引入的一個非常有意思的功能,它主要用于解決想要在原有函數(shù)或類的基礎(chǔ)上進(jìn)行功能擴(kuò)展,但又不會破壞這個函數(shù)本身的功能。并且我們可以方便的添加和去除這一部分?jǐn)U展的功能的需求。

​ 例如:當(dāng)你在調(diào)試代碼的時候,你想要給一些功能函數(shù)添加打印調(diào)試信息。但是,這個功能只在我們調(diào)試的時候使用,正式發(fā)布的時候是不需要的。如果此時,你在函數(shù)內(nèi)部修改,由于有多個函數(shù)都需要添加相同的或類似的信息,那么,你就需要逐一修改,這個就有點(diǎn)麻煩了,此時我們就可以使用裝飾器來解決這一問題。

​ 在解釋如何使用裝飾器之前,我們需要先把和裝飾器有關(guān)的基本概念給講一下。

Python 函數(shù)的基本特性

函數(shù)名的本質(zhì):

在 python 中,一切皆是對象,也就是說,我們定義的變量和函數(shù)都是一個對象。而是對象就意味著我們可以獲得這個對象的屬性,例如函數(shù)對象有一個 __name__ 的屬性:

def function(): #定義一個函數(shù)
    print('this is a function !')

function()
print(function) #打印函數(shù)名的地址
print(function.__name__) #打印函數(shù)名

a = function #把函數(shù)賦給一個變量
a()
print(a) #打印 a 的地址
print(a.__name__) #再次打印函數(shù)名

​ 打印結(jié)果:

this is a function !
<function function at 0x0000029F83C17F70>
function
this is a function !
<function function at 0x0000029F83C17F70>
function

由打印可以看出,我們的函數(shù)名在賦給另一個變量的時候,其函數(shù)地址和函數(shù)屬性中的函數(shù)名是沒有變化的。也就是說,當(dāng)我們在定義函數(shù)的時候,我們的函數(shù)名和普通的變量是一樣的,唯一的不同就是,我們的函數(shù)名會指向一個內(nèi)存空間,而這片空間內(nèi)保存的是一個函數(shù)體的內(nèi)容。

​ 但是,當(dāng)我們把這個函數(shù)名賦值給其他變量的時候,這個函數(shù)名就會把它執(zhí)行的內(nèi)存空間的地址賦值給另一個變量,因此,另一個變量也就成為了一個函數(shù)了。

​ 這里我們已經(jīng)能夠注意到了,函數(shù)名如果不加 () 那么它和普通的變量一樣,而加了 () 之后,它就會去執(zhí)行我們的函數(shù)內(nèi)容。

​ 這里我們把試著刪除我們定義時候使用的函數(shù)名:

del function #刪除 function 函數(shù)
a() #執(zhí)行 a()
print(a) #打印出 a 指向的地址
print(a.__name__) #打印a的函數(shù)名

function()
print(function)
print(function.__name__)

​ 查看打印:

this is a function !
<function function at 0x000002258DC17F70>
function
NameError: name 'function' is not defined

​ 可以看到,我們的 function() 函數(shù)名提示沒有定義,但是我們的 a() 函數(shù)卻可以正常的打印出來。這里的 del 其實(shí)就是把我們的 function 這個函數(shù)名的指針給指向的函數(shù)地址給刪去了,此時它變成立一個真正的未定義的變量了。

將函數(shù)作為變量使用:

​ 既然函數(shù)名和普通的變量可以相互賦值,那就說明,我們也可以像使用普通變量一樣使用函數(shù)名了。

在函數(shù)中定義函數(shù):

​ 我們可以像定義普通變量一樣,在一個函數(shù)中定義另一個函數(shù):

def function1():
    print('this is function 1')
    def function2():
        print('this is function 2 !')
        return 0
    function2()
    return 0

function1()
function2()

​ 打印如下:

this is function 1
this is function 2 !
NameError: name 'function2' is not defined

​ 可以看到,我們在 function1 中定義了一個 function2 函數(shù),而且在 function1 中使用了 function2 這個函數(shù)。但是,當(dāng)我們在外面使用 function2 這個函數(shù)的時候,卻打印了該函數(shù)未定義。這里說明,函數(shù)內(nèi)定義的函數(shù)的作用域也僅限于函數(shù)內(nèi)部,和我們的局部變量是一樣的。

​ 但是,當(dāng)我們把函數(shù)作為返回值的時候,這個情況就不一樣了,這里參考我上一篇文章:Python中的閉包中的變量作用域問題

在函數(shù)中返回函數(shù)名:

既然我們可以在一個函數(shù)中定義另一個函數(shù),那么也就可以在函數(shù)中返回另一個函數(shù):

def function1():
    print('this is function 1')
    def function2():
        print('this is function 2 !')
        return 0
    function2() #在函數(shù)內(nèi)部使用該函數(shù)
    return function2 #返回該函數(shù)的函數(shù)名

a = function1() #把函數(shù)名返回給一個變量
a()

​ 打印如下:

this is function 1
this is function 2 !
this is function 2 !

​ 這里可以看到,我們的這個在函數(shù) function1 中定義并返回了函數(shù) function2 并在外部使用一個變量來接收 funciton1 的返回值。由此可以看出,函數(shù)名和變量的使用方式差別不大。

​ 注意: 雖然我們說的時候會說在一個函數(shù)中返回另一個函數(shù),但是,實(shí)際上,我們返回的只是這個函數(shù)的函數(shù)名(不帶括號'()‘)。

把函數(shù)名作為參數(shù)使用:

def hello():
    print("hello")

def function1(func): #接收一個參數(shù)
    print('before call hello !')
    func()
    print('after call hello !')
    #function2() #在函數(shù)內(nèi)部使用該函數(shù)

function1(hello) #把 hello 作為參數(shù)傳遞進(jìn)去

​ 打印如下:

before call hello !
hello
after call hello !

​ 由打印可以知道,我們在函數(shù) function1 中定義的接收參數(shù) func 我們在定義的時候并沒有采用什么特殊的方式,而是和普通參數(shù)一樣定義。之后,在外部調(diào)用 function1 的使用,把函數(shù)名 hello 當(dāng)作參數(shù)傳遞進(jìn)去了。隨后,我們運(yùn)行 function1 并在 function1 中成功調(diào)用了 hello 函數(shù)。

進(jìn)一步實(shí)現(xiàn)裝飾器

​ 現(xiàn)在,讓我們再重新看一下什么是裝飾器,我們在上面的把函數(shù)名作為參數(shù)使用時,已經(jīng)實(shí)現(xiàn)了一個和裝飾器功能類似的函數(shù)了。假如我們的 hello() 函數(shù)是我們的功能函數(shù),而 function1 作為我們的裝飾器,那么,我們成功實(shí)現(xiàn)了在不改變 hello() 函數(shù)的基礎(chǔ)上,通過把它作為參數(shù)使用而增加了其他的打印內(nèi)容。

​ 雖然我們上面實(shí)現(xiàn)了一個類似裝飾器的功能,但是,我們可以看到,使用這個的時候我們需要每次都給 function1 傳入一個函數(shù),這樣使用就很麻煩了。下面我們改造一下這個函數(shù):

def decorator(func): #裝飾器函數(shù),用于接收一個函數(shù)參數(shù)
    def wrapper(): #定義一個內(nèi)函數(shù) wrapper 
        print('before call hello !')
        func() 
        print('after call hello !')
    return wrapper #把內(nèi)函數(shù)做未返回值

def hello():
    print("hello")
    
hello = decorator(hello) #重新定義一個函數(shù) hello1
hello() #執(zhí)行 hello

​ 打印如下:

before call hello !
hello
after call hello !

​ 通過上面的打印可以看到,我們新更改的這個函數(shù)可以實(shí)現(xiàn)和上面的函數(shù)一樣的功能。但是,我們這里在使用它的時候比之前要簡單一些,因?yàn)槲覀兛梢灾苯邮褂门f的函數(shù)名來使用新的功能 (這里我們相當(dāng)于給函數(shù)名 hello 賦值了一個新的函數(shù)wrapper),當(dāng)我們想要使用舊函數(shù)時,只需要把 hello=function(hello) 這行內(nèi)容給注釋掉就可以了。

使用Python裝飾器語句:

簡單裝飾器

​ 上面的我們已經(jīng)實(shí)現(xiàn)了一個裝飾器的功能,下面我們使用 Python 中自帶的裝飾器來測試一下:

def decorator(func): #裝飾器函數(shù),用于接收一個函數(shù)參數(shù)
    def wrapper(): #定義一個內(nèi)函數(shù) wrapper 
        print('before call hello !')
        func() 
        print('after call hello !')
    return wrapper #把內(nèi)函數(shù)做為返回值

@decorator # '@' 是系統(tǒng)自帶的裝飾器語法糖
def hello():
    print('hello')

hello()

​ 打印如下:

before call hello !
hello
after call hello !

​ 可以看到,我們使用系統(tǒng)自帶得裝飾器語法實(shí)現(xiàn)了和我們上面得函數(shù)一樣得功能。

​ 這里我們可以看到,他們兩個唯一得不同就是使用了 @decorator這個符號來代替了 hello=decorator(hello) 這個賦值語句。

​ 到這里,其實(shí)我們基本上就已經(jīng)明白了python所謂得裝飾器的原理和實(shí)際用法了,但是,這里我們還有一個問題,那就是這種方法會改變我們的函數(shù)的屬性嗎?

​ 我們測試一下:

print(hello.__name__)

​ 打印如下:

wrapper

​ 很明顯,我們的原函數(shù)的屬性中的函數(shù)名被更改了,其實(shí)通過上面自己的實(shí)現(xiàn),我們可以發(fā)現(xiàn),我們使用裝飾器語法其實(shí)就是新建了一個函數(shù)名,然后用它去接收裝飾器函數(shù)的返回函數(shù)名,這樣,該該函數(shù)肯定還是繼承了裝飾器返回函數(shù)的函數(shù)名了。

​ 為了解決這個問題,我們可以使用如下方法:

import functools 

def function(func): #接收一個參數(shù)
    @functools.wraps(func)
    def wrapper(): #定義一個內(nèi)函數(shù) wrapper 
        print('before call hello !')
        func() 
        print('after call hello !')
    return wrapper #把內(nèi)函數(shù)做為返回值

@function # '@' 是系統(tǒng)自帶的裝飾器語法糖
def hello():
    print('hello')

hello()
print(hello.__name__)

​ 打印如下:

before call hello !
hello
after call hello !
hello

​ 通過我們使用系統(tǒng)模塊,我們解決了這一問題。

帶參數(shù)裝飾器:

上面我們展示了裝飾器的基礎(chǔ)用法,但是,我們可以發(fā)現(xiàn)一個問題,那就是這個裝飾器只能用于打印一類的基本操作,有時我們需要在裝飾器函數(shù)內(nèi)傳參,且需要在多個函數(shù)中使用同一個裝飾器函數(shù),如果單純使用上面的方法就不太容易操作了。

​ 下面我們展示一種給裝飾器傳參的操作方法:

import functools

def logging(level):#裝飾器接收參數(shù)函數(shù)
    def decorator(func): #裝飾器函數(shù),用于接收一個函數(shù)
        @functools.wraps(func)
        def wrapper(*args, **kwargs): #定義一個內(nèi)函數(shù) wrapper 
            if level == 'warn':
                print('warn: before call %s !' %func.__name__)
                func() 
                print('warn: after call %s !' %func.__name__)
            if level == 'error':
                print('error: before call %s !' %func.__name__)
                func() 
                print('error: after call %s !' %func.__name__)
        return wrapper #把內(nèi)函數(shù)做為返回值
    return decorator

@logging(level='warn') # '@' 是系統(tǒng)自帶的裝飾器語法糖
def hello():
    print('hello')

@logging(level='error')
def function1():
    print('function1')

hello()
function1()
print(hello.__name__)
print(function1.__name__)

​ 打印如下:

warn: before call hello !
hello
warn: after call hello !
error: before call function1 !
function1
error: after call function1 !
hello
function1

​ 可以看到,我們在兩個函數(shù)中使用了一個裝飾器語法,而且給這個裝飾器分別傳了不同的參數(shù),這個才比較符號我們實(shí)際可能會用到的情況。

​ 這里第一次看可能感覺有點(diǎn)復(fù)雜,而且我們在這里也使用了多層函數(shù)嵌套,每層都傳不同的參數(shù)。這里我來仔細(xì)拆分一下這個函數(shù):

​ 首先我們知道:

@logging
def hello():
     print('hello')
#等價于
logging(hello)

​ 因此:

@logging(level='warn')
def hello():
     print('hello')
#等價于
logging(hello)(level='warn')

​ 下面我們繼續(xù)拆解 logging(hello)(level=‘warn') 這句話:

logging(hello)(level='warn')

由于
 logging(hello) 返回 decorator
于是
 logging(hello)(level='warn')
    等價于
    decorator(level='warn')

 decorator 返回 wrapper
    因此
 這里其實(shí)就到了我們最上面的簡單裝飾器了

​ 到這里我們就明白了我們的裝飾器傳參是怎么回事了。

裝飾器類:​

由于我們在 python 中會經(jīng)常使用類來對某一功能進(jìn)行封裝,這樣,當(dāng)我們在使用某一功能的時候就更加靈活且方便了。

​ 因此,我們的 python 也給我們提供了實(shí)現(xiàn)裝飾器類的使用方法:

class Logging(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('class: before call %s !' %self._func.__name__)
        self._func() 
        print('class: after call %s !' %self._func.__name__)

@Logging
def hello():
    print('Hello')

hello()

​ 打印如下:

class: before call hello !
Hello
class: after call hello !

​ 可以看到,我們的類裝飾器的用法和函數(shù)類似,只是在定義裝飾器函數(shù)的時候,把函數(shù)的實(shí)現(xiàn)變成了類方法的實(shí)現(xiàn)方式。

​ 除了這種最基本的的使用方式,我們其實(shí)也可以給類裝飾器傳參:

class Logging(object):
    def __init__(self, level='INFO'):
        self._level = level

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            if self._level == 'WARN':
                print('class: warn before call %s !' %func.__name__)
                func() 
                print('class: warn after call %s !' %func.__name__)
        return wrapper

@Logging(level='WARN')
def hello():
    print('Hello')

hello()

​ 打印如下:

class: warn before call hello !
Hello
class: warn after call hello !

​ 這里傳參方式和上面直接在類中的 __call__ 中定義函數(shù)有些不一樣,這里需要記住兩點(diǎn):

__init__:不再接收被裝飾函數(shù),而是接收傳入?yún)?shù);
__call__:接收被裝飾函數(shù),實(shí)現(xiàn)裝飾邏輯

​ 這里就不對這個類方法進(jìn)行深入解析了。

總結(jié)

到此這篇關(guān)于Python中裝飾器的基本功能的文章就介紹到這了,更多相關(guān)Python裝飾器功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • OpenCV Python實(shí)現(xiàn)拼圖小游戲

    OpenCV Python實(shí)現(xiàn)拼圖小游戲

    這篇文章主要為大家詳細(xì)介紹了OpenCV Python實(shí)現(xiàn)拼圖版小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Python列表pop()函數(shù)使用實(shí)例詳解

    Python列表pop()函數(shù)使用實(shí)例詳解

    這篇文章主要介紹了Python列表pop()函數(shù)使用實(shí)例詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • python 解決函數(shù)返回return的問題

    python 解決函數(shù)返回return的問題

    這篇文章主要介紹了python 解決函數(shù)返回return的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Python控制windows系統(tǒng)音量實(shí)現(xiàn)實(shí)例

    Python控制windows系統(tǒng)音量實(shí)現(xiàn)實(shí)例

    這篇文章主要介紹了Python控制windows系統(tǒng)音量實(shí)現(xiàn)實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-01-01
  • 如何利用Python批量處理行、列和單元格詳解

    如何利用Python批量處理行、列和單元格詳解

    因?yàn)橹蟮墓ぷ骺赡軙?jīng)常用到excel,而且也不想荒廢Python,出于為以后做準(zhǔn)備以防不時之需,下面這篇文章主要給大家介紹了關(guān)于如何利用Python批量處理行、列和單元格的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • 使用python實(shí)現(xiàn)ANN

    使用python實(shí)現(xiàn)ANN

    這篇文章主要為大家詳細(xì)介紹了使用python實(shí)現(xiàn)ANN的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • python3 中文亂碼與默認(rèn)編碼格式設(shè)定方法

    python3 中文亂碼與默認(rèn)編碼格式設(shè)定方法

    今天小編就為大家分享一篇python3 中文亂碼與默認(rèn)編碼格式設(shè)定方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • python基礎(chǔ)教程之Hello World!

    python基礎(chǔ)教程之Hello World!

    這篇文章主要介紹了python基礎(chǔ)教程之Hello World!,本文講解了命令行中、文件中、腳本文件中輸出Hello World的例子,需要的朋友可以參考下
    2014-08-08
  • python實(shí)現(xiàn)密碼強(qiáng)度校驗(yàn)

    python實(shí)現(xiàn)密碼強(qiáng)度校驗(yàn)

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)密碼強(qiáng)度校驗(yàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • python excel多行合并的方法

    python excel多行合并的方法

    這篇文章主要介紹了python excel多行合并的方法,幫助大家更好的利用python處理excel表格,感興趣的朋友可以了解下
    2020-12-12

最新評論