真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Python-裝飾器的入門(mén)講解

裝飾器的萬(wàn)能公式

小白在學(xué)習(xí)裝飾器時(shí),會(huì)遇到一些地方不太理解或者不太清楚,這是因?yàn)橐婚_(kāi)始你就直接擼裝飾器的緣故,那么怎樣才能將裝飾器理解并且弄懂呢?
所以在學(xué)裝飾器之前必須要弄懂函數(shù)的嵌套以及閉包,接下來(lái)我用嵌套--->閉包--->裝飾器這個(gè)順序來(lái)講解,希望對(duì)各位有用。

創(chuàng)新互聯(lián)2013年至今,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都做網(wǎng)站、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元梁山做網(wǎng)站,已為上家服務(wù),為梁山各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220

函數(shù)的嵌套

1、首先要弄懂,函數(shù)的作用域的概念:Python中以函數(shù)為作用域,在作用域中定義的相關(guān)數(shù)據(jù)只能被當(dāng)前作用域或子作用域使用。
同時(shí)函數(shù)也是定義在作用域中的數(shù)據(jù),在執(zhí)行函數(shù)時(shí)候,也同樣遵循:優(yōu)先在自己作用域中尋找,沒(méi)有則向上一作用域?qū)ふ?/p>

def func():
    print("你好")
    
func()

def execute():
    print("開(kāi)始")
    func()
    print("結(jié)束")

def func():
    print(666)

func()
execute()

認(rèn)真執(zhí)行上面的代碼,你就能發(fā)現(xiàn)函數(shù)在全局作用域中的一些規(guī)則。也就是函數(shù)名相同的函數(shù),新的函數(shù)會(huì)覆蓋就的函數(shù)。
2、上述示例中的函數(shù)均定義在全局作用域,其實(shí)函數(shù)也可以定義在局部作用域,這樣函數(shù)被局部作用域和其子作用于中調(diào)用(函數(shù)的嵌套)。

def func():
    print("")

def inner():
    print("-------------")

def handler():
    print("")
    def inner():
        print("")
    inner()
    func()
    print("")

handler()

分析上面可知:函數(shù)先調(diào)用自己作用域內(nèi)部的函數(shù),若作用域內(nèi)部沒(méi)有,則從外部去找。
現(xiàn)在的你可能有疑問(wèn):為什么要這么嵌套定義?把函數(shù)都定義在全局不好嗎?
其實(shí),大多數(shù)情況下我們都會(huì)將函數(shù)定義在全局,不會(huì)嵌套著定義函數(shù)。不過(guò),當(dāng)我們定義一個(gè)函數(shù)去實(shí)現(xiàn)某功能,想要將內(nèi)部功能拆分成N個(gè)函數(shù),又擔(dān)心這個(gè)N個(gè)函數(shù)放在全局會(huì)與其他函數(shù)名沖突時(shí)(尤其多人協(xié)同開(kāi)發(fā))可以選擇使用函數(shù)的嵌套。

嵌套引發(fā)的作用域的問(wèn)題

1、基于內(nèi)存和執(zhí)行過(guò)程分析作用域。
直接看2個(gè)例子,基本就可以理解了,我這邊并沒(méi)有去運(yùn)行,可以的話,大家最好去親自運(yùn)行,看看與自己理解的是不是一樣。
這邊要注意一個(gè)問(wèn)題:外部函數(shù)在return返回內(nèi)部的函數(shù)名時(shí),并沒(méi)有執(zhí)行函數(shù),而是只返回函數(shù)名,僅此而已??聪旅娴睦樱?/p>

name = "dack"

def run():
    name = "dack_deng"
    def inner():
        print(name)
    return inner  # run函數(shù)僅僅只是返回的一個(gè)函數(shù)名而已
    
v1 = run()  # 現(xiàn)在v1是執(zhí)行了run()函數(shù),返回inner這個(gè)函數(shù)名,即v1==inner
v1()  # v1()==inner(),開(kāi)始執(zhí)行函數(shù)inner,輸出“dack_deng”

v2 = run()  # 同理
v2()

3、總結(jié):
三句話搞定作用域:

  • 優(yōu)先在自己的作用域找,自己沒(méi)有就去上級(jí)作用域。
  • 在作用域中尋找值時(shí),要確保此次此刻值是什么。
  • 分析函數(shù)的執(zhí)行,并確定函數(shù)作用域鏈。(函數(shù)嵌套)

閉包

閉包,簡(jiǎn)而言之就是將數(shù)據(jù)封裝在一個(gè)包(區(qū)域)中,使用時(shí)再去里面取。(本質(zhì)上閉包是基于函數(shù)嵌套搞出來(lái)一個(gè)特殊的嵌套)
也是看下面2個(gè)例子:

"""例子1"""
def task(arg):
    def inner():
        print(arg)
    return inner  # 函數(shù)僅僅只是返回inner函數(shù)名而已

v1 = task(11)  # v1 == inner,此時(shí)agr == 11
v2 = task(22)  # v2 == inner,此時(shí)agr == 22
v3 = task(33)  # v3 == inner,此時(shí)agr == 33
v1()  # 調(diào)用函數(shù)inner(),此時(shí)agr == 11,輸出:11
v2()  # 調(diào)用函數(shù)inner(),此時(shí)agr == 22,輸出:22
v3()  # 調(diào)用函數(shù)inner(),此時(shí)agr == 33,輸出:33

"""例子2"""
def task(arg):
    def inner():
        print(arg)
    return inner

inner_func_list = []
for val in [11,22,33]:
    inner_func_list.append( task(val) )  # 執(zhí)行玩for循環(huán)之后:inner_func_list == [inner, inner, inner]
    
inner_func_list[0]() # 11  # 函數(shù)調(diào)用inner_func_list中的第一個(gè)函數(shù),并且第一函數(shù)調(diào)用時(shí)arg==11,輸出:11
inner_func_list[1]() # 22  # 函數(shù)調(diào)用inner_func_list中的第一個(gè)函數(shù),并且第一函數(shù)調(diào)用時(shí)arg==22,輸出:22
inner_func_list[2]() # 33  # 函數(shù)調(diào)用inner_func_list中的第一個(gè)函數(shù),并且第一函數(shù)調(diào)用時(shí)arg==33,輸出:33

裝飾器的萬(wàn)能公式

通過(guò)上面的函數(shù)嵌套和閉包的理解,接下來(lái)講裝飾器基本更好理解了。
裝飾器就是在不修改原函數(shù)內(nèi)容的前提下,通過(guò)@函數(shù)可以實(shí)現(xiàn)在函數(shù)前后自定義執(zhí)行一些功能(批量操作會(huì)更有意義),類(lèi)似于自動(dòng)化測(cè)試中的前置和后置,這就更好理解了它是個(gè)啥東西了。
話不多說(shuō),先將裝飾器之前,先上裝飾器的萬(wàn)能公式,所有的裝飾器都是基于這個(gè)公式去套用的。

"""裝飾器的萬(wàn)能公式"""
def outer(origin):
    def inner(*args, **kwargs):  # 這里的*args和**kwargs不固定,可根據(jù)自己的項(xiàng)目中去指定
        # 執(zhí)行前
        res = origin(*args, **kwargs)  #調(diào)用原來(lái)的func函數(shù)
        # 執(zhí)行后
        return res
    return inner

@outer  # 一定要記住這個(gè):func = outer(func) ==========> func == innner
def func():
    pass

func()

接下來(lái)直接上題:

"""1, 請(qǐng)為以下所有函數(shù)編寫(xiě)一個(gè)裝飾器,添加上裝飾器后可以實(shí)現(xiàn),執(zhí)行func時(shí),先執(zhí)行func函數(shù)內(nèi)部代碼
再輸出”after“"""
def outer(origin):
    def inner(*args, **kwargs):
        res = origin(*args, **kwargs)
        print("after")
        return res
    return inner
@outer
def func(a1):
    print("func")
    return a1 + "哈哈"

@outer
def base(a1, a2):
    print("base")
    return a1 + a2 + "呵呵"

@outer
def foo(a1, a2, a3, a4):
    print("foo")
    return a1 + a2 + a3 + a4 + "嘻嘻"

func("1")
base("1", "2")
foo("1","2","3","4")

"""2, 編寫(xiě)裝飾器,添加上裝飾器后實(shí)現(xiàn):將被裝飾的函數(shù)執(zhí)行5次,將每次執(zhí)行函數(shù)的結(jié)果按照順序放到
列表中,最終返回列表"""
import random

def outer(origin):
    def inner(*args, **kwargs):
        print("before")
        res = origin(*args, **kwargs)
        print("after")
        return res
    return inner
@outer
def func():
    return random.randint(1, 4)
list = []
for i in range(5):
    print(i)
    result = func()
    list.append(result)# 內(nèi)部自動(dòng)執(zhí)行5次,并將每次執(zhí)行的結(jié)果追加到列表最終返回給result
print(list)

"""3, 編寫(xiě)函數(shù)裝飾器,添加上裝飾器后可以實(shí)現(xiàn):檢查文件所在路徑(文件夾)是否存在,如果不存在
自動(dòng)創(chuàng)建文件夾(保證寫(xiě)入文件不報(bào)錯(cuò))"""
import os
file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'usr', 'bin', 'xxx')
def outer(origin):
    def inner(*args, **kwargs):
        # 函數(shù)執(zhí)行前的操作
        if not os.path.exists(file_path):
            os.makedirs(file_path)
        res = origin(*args, **kwargs)  # 執(zhí)行被裝飾的原函數(shù)
        # 執(zhí)行完函數(shù)的下一步操作
        return res
    return inner
@outer
def writer_user_info(path):
    with open(path, mode='w', encoding='utf-8') as file_obj:
        for i in range(10):
            file_obj.write("dack\n")

writer_user_info('usr/bin/xxx/xxx.txt')

總結(jié):
我的那種寫(xiě)法就稱(chēng)為裝飾器。

  • 實(shí)現(xiàn)原理:基于@語(yǔ)法和函數(shù)閉包,將原函數(shù)封裝在閉包中,然后將函數(shù)賦值為一個(gè)新的函數(shù)(內(nèi)層函數(shù)),執(zhí)行函數(shù)時(shí)再在內(nèi)層函數(shù)中執(zhí)行閉包中的原函數(shù)。

  • 實(shí)現(xiàn)效果:可以在不改變?cè)瘮?shù)內(nèi)部代碼 和 調(diào)用方式的前提下,實(shí)現(xiàn)在函數(shù)執(zhí)行和執(zhí)行擴(kuò)展功能。

  • 適用場(chǎng)景:多個(gè)函數(shù)系統(tǒng)統(tǒng)一在 執(zhí)行前后自定義一些功能

裝飾器的重要補(bǔ)充:functools

先看下面這些實(shí)例:

"""示例1"""
def handler():
    pass

handler()
print(handler.__name__) # handler

"""示例2"""
def auth(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def handler():
    pass

handler()
print(handler.__name__) # inner

"""示例3"""
import functools

def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def handler():
    pass

handler()
print(handler.__name__)  # handler

所以,一般情況下大家不用functools也可以實(shí)現(xiàn)裝飾器的基本功能,但后期在項(xiàng)目開(kāi)發(fā)時(shí),不加functools會(huì)出錯(cuò)(因?yàn)閮?nèi)部會(huì)讀取__name__,且__name__重名的話就報(bào)錯(cuò)),所以在此大家就要規(guī)范起來(lái)自己的寫(xiě)法。
所以裝飾器的萬(wàn)能公式,我們就可以更新如下所示:

import functools

def outer(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
         # 函數(shù)執(zhí)行前的操作
        res = func(*args, **kwargs)  # 執(zhí)行原函數(shù)
         # 函數(shù)執(zhí)行后的操作
        return res
    return inner
@outer  # func = outer(func)
func()
  pass

func()

關(guān)于裝飾器的知識(shí),到此全部講完啦,希望對(duì)大家有所幫助! 如果有不對(duì)的地方,歡迎各位小伙伴指出,感謝!


新聞標(biāo)題:Python-裝飾器的入門(mén)講解
網(wǎng)頁(yè)網(wǎng)址:http://weahome.cn/article/dsojcch.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部