成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

python奇遇記:深入理解裝飾器

lemon / 2319人閱讀

摘要:可見(jiàn)裝飾器改變了函數(shù)的功能。裝飾器除了改變函數(shù)功能之外還有一個(gè)特性是,函數(shù)裝飾器在導(dǎo)入模塊時(shí)立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時(shí)運(yùn)行。

什么是裝飾器

裝飾器是什么,簡(jiǎn)單來(lái)說(shuō),裝飾器可以改變一個(gè)函數(shù)的行為,比如原本有一個(gè)函數(shù)用來(lái)計(jì)算菲波那切數(shù)列,我們給這個(gè)函數(shù)加個(gè)計(jì)算執(zhí)行時(shí)間的裝飾器,這樣原來(lái)的函數(shù)不僅能夠計(jì)算菲波那切數(shù)列,而且還可以輸出計(jì)算花費(fèi)了多少時(shí)間。

在Python中,有幾個(gè)很常見(jiàn)的內(nèi)置裝飾器:比如@staticmethod, 它可以將一個(gè)類的方法聲明為靜態(tài)的。@property, 為類中的變量設(shè)置get和set方法,保證了封裝性。

如果你使用過(guò)python的web框架(比如flask)開(kāi)發(fā)過(guò)網(wǎng)站,你應(yīng)該經(jīng)常會(huì)見(jiàn)到裝飾器,像下面這樣:

@app.route("/")
def hello():
    return "Hello World!"

這段代碼把路由綁定到hello函數(shù)上,這樣你輸入網(wǎng)址之后就可以看到Hello World 。

先來(lái)看個(gè)很簡(jiǎn)單的例子:

# 定義了一個(gè)裝飾器
def deco(func):
  def hah():
      print("hahha")
  return hah

上面我們定義了一個(gè)裝飾器,打印hahah,接下來(lái)使用:

# 使用這個(gè)裝飾器
@deco
def lal():
    pritn("lalalala")

lal()

執(zhí)行l(wèi)al()會(huì)輸出hahha。 可見(jiàn)deco裝飾器改變了lal函數(shù)的功能。上面的代碼中,我們實(shí)際上是把lal函數(shù)放入了deco函數(shù),像這樣:

lal = deco(lal)

只不過(guò),直接使用@標(biāo)志把裝飾器放在某個(gè)函數(shù)上更方便一點(diǎn)而已。

裝飾器其實(shí)就是一個(gè)函數(shù)嵌套另一個(gè)函數(shù)(這里涉及到一個(gè)概念叫做閉包,下面會(huì)講到)。在裝飾器的定義中,需要把內(nèi)部的函數(shù)返回(像hah),內(nèi)部函數(shù)用來(lái)真正的改變被裝飾函數(shù)的功能。

不過(guò),上面定義的裝飾器好像沒(méi)什么用,我們來(lái)真正的寫(xiě)一個(gè)裝飾器,像文章開(kāi)頭說(shuō)的那樣,定義一個(gè)裝飾器計(jì)算函數(shù)執(zhí)行的時(shí)間。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的裝飾器
import time
# 這個(gè)裝飾器接收一個(gè)函數(shù)作為參數(shù)
def clock(func):
  # clocked用來(lái)改變被裝飾函數(shù)功能
  # 接收任意可變參數(shù)
    def clocked(*args):
      #先計(jì)算時(shí)間
        t0 = time.perf_counter()
        # 然后運(yùn)行被裝飾的函數(shù)
        result = func(*args)
        # 計(jì)算運(yùn)行前后的時(shí)間差
        elapsed = time.perf_counter()-t0
        # 函數(shù)的名字
        name = func.__name__
        # 被裝飾函數(shù)的所有變量
        arg_str = ",".join(repr(arg) for arg in args)
        # 輸出
        print("[%0.8fs] %s(%s) -> %r" % (elapsed, name, arg_str, result))
        # 返回被裝飾函數(shù)執(zhí)行結(jié)果
        # 可見(jiàn)裝飾器是在原來(lái)的函數(shù)上增加了某些功能
        # 而不是完全改變被裝飾函數(shù)
        return result
     # 把clocked函數(shù)返回
    return clocked

來(lái)使用一下上面定義的裝飾器:

@clock
def factorial(n):
    return 1 if n<2 else n*factorial(n-1)
  
result = factorial(6)
print(result)

執(zhí)行結(jié)果:

[0.00000030s] factorial(1) -> 1
[0.00004588s] factorial(2) -> 2
[0.00007184s] factorial(3) -> 6
[0.00060794s] factorial(4) -> 24
[0.00064205s] factorial(5) -> 120
[0.00066801s] factorial(6) -> 720
720

可以看到,在輸出計(jì)算結(jié)果的同時(shí),輸出了每一步的執(zhí)行時(shí)間。

裝飾器除了改變函數(shù)功能之外還有一個(gè)特性是,函數(shù)裝飾器在導(dǎo)入模塊時(shí)立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時(shí)運(yùn)行。這點(diǎn)需要注意。

當(dāng)然了,裝飾器之上還可以放一個(gè)裝飾器,不過(guò)是多了一層嵌套而已。

python中還有一個(gè)內(nèi)置的模塊functools,這里面定義了一些常用的裝飾器函數(shù),幫助你更好地定義自己的裝飾器。這里就不講了。

閉包

說(shuō)到閉包,在上面的代碼中我們已經(jīng)見(jiàn)識(shí)到了,函數(shù)中嵌套函數(shù)就是閉包。嚴(yán)格來(lái)說(shuō),閉包是指延伸了作用域的函數(shù),怎么理解?不如來(lái)看個(gè)例子:

我們定義一個(gè)函數(shù)不斷計(jì)算平均值,它會(huì)記住上一次計(jì)算的值進(jìn)行累計(jì)。

# 先看一些效果
avg = make_averager()
print(avg(10))
print(avg(11))
print(avg(12))

輸出如下:

10.0
10.5
11.0

第一次輸出10,第二次輸出10加11的平均值,第三次輸出10加11加12的平均值。

怎么實(shí)現(xiàn)的?

def make_averager():
  # 局部變量series
  # 用來(lái)保存每次輸入的值
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
    return averager

上面的函數(shù)中,series是局部變量。當(dāng)我們調(diào)用avg(10)的時(shí)候,函數(shù)已經(jīng)返回了,按理說(shuō)它的本地作用域已經(jīng)不存在了,但是我們還是可以繼續(xù)使用。這是因?yàn)閟eries其實(shí)是自由變量,它不受本地作用域的限制。需要注意的是,對(duì)于不可變類型,需要顯示用關(guān)鍵字nonlocal 聲明自由變量,如果不聲明的話,會(huì)隱式的創(chuàng)建局部變量,這樣自由變量就會(huì)失效。而可變類型則不需要。比如,我們來(lái)更改一下上面的代碼:

# 改一下求平均值的函數(shù)
# 用另一種方法
def make_averager():
   count = 0
   total = 0
   def averager(new_value):
      # count、total是不可變類型
      # 需要聲明為自由變量
     nonlocal count, total
     count += 1
     total += new_value
     return total / count
   return averager

除了上面說(shuō)的裝飾器的用法之外,我們還可以為裝飾器添加參數(shù),像app.route("/") 這樣,限于篇幅,下一篇文章再介紹。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/41224.html

相關(guān)文章

  • python奇遇:隱藏的python功能

    摘要:先不講數(shù)據(jù)結(jié)構(gòu)了,這次來(lái)說(shuō)說(shuō)中一些不被注意的功能。直接交換第二個(gè)功能。對(duì)的長(zhǎng)度使用生成一個(gè)序列,然后遍歷或者這樣第三個(gè)功能。其實(shí)還接受第二個(gè)參數(shù),它的作用是在迭代的過(guò)程中如果碰到第二個(gè)參數(shù)則停止。 先不講數(shù)據(jù)結(jié)構(gòu)了,這次來(lái)說(shuō)說(shuō)python中一些不被注意的功能。 在python的設(shè)計(jì)哲學(xué)中,有這么一條內(nèi)容:Simple is better than complex,簡(jiǎn)單的代碼比復(fù)雜的要好...

    APICloud 評(píng)論0 收藏0
  • python奇遇:迭代和生成

    摘要:來(lái)說(shuō)說(shuō)迭代器和生成器,還有可迭代對(duì)象和生成器表達(dá)式。有點(diǎn)繞是不是,其實(shí),一般只要知道可迭代對(duì)象以及它是如何實(shí)現(xiàn)的就行了,中常常用生成器來(lái)代替迭代器,可以說(shuō),生成器就是迭代器。 來(lái)說(shuō)說(shuō)迭代器和生成器,還有可迭代對(duì)象和生成器表達(dá)式。 之前簡(jiǎn)單的提到過(guò),一個(gè)對(duì)象是可迭代的可以理解為能夠使用for循環(huán)。這樣說(shuō)其實(shí)不太準(zhǔn)確,某個(gè)對(duì)象可迭代是因?yàn)樗鼉?nèi)部實(shí)現(xiàn)了$__iter__$這個(gè)特殊方法。比如在...

    atinosun 評(píng)論0 收藏0
  • Python奇遇:數(shù)據(jù)結(jié)構(gòu)窺探2

    摘要:找出列表中小于的數(shù)據(jù)除了列表推導(dǎo)式,還有字典推導(dǎo)式,集合推導(dǎo)式,用法都一樣。如果你的數(shù)據(jù)量很大的話,考慮使用生成器表達(dá)式。切片不僅對(duì)列表有用,同樣適用于元組和字符串。切片命名使用方法,內(nèi)部參數(shù)與切片一樣。對(duì)剩余的的數(shù)據(jù),使用星號(hào)代替即可。 上次我們講了幾個(gè)不常見(jiàn)的數(shù)據(jù)類型,每個(gè)都有自己特殊的用途,雖然不經(jīng)常用到,了解一下也好。比如我們提到的數(shù)組類型,如果在數(shù)據(jù)量很大的時(shí)候同時(shí)要效率,就...

    Ocean 評(píng)論0 收藏0
  • python奇遇:數(shù)據(jù)結(jié)構(gòu)窺探3

    摘要:字典和集合都是基于散列表實(shí)現(xiàn)的,散列表也就是表,了解過(guò)數(shù)據(jù)結(jié)構(gòu)的應(yīng)該知道。而使用另一種辦法,任何鍵在找不到的情況下都會(huì)用中的值數(shù)據(jù)類型比如替換。在設(shè)計(jì)時(shí)就可以使用創(chuàng)建你的數(shù)據(jù)接口。 這次主要說(shuō)說(shuō)字典和集合這兩種數(shù)據(jù)類型。 字典和集合都是基于散列表實(shí)現(xiàn)的,散列表也就是hash表,了解過(guò)數(shù)據(jù)結(jié)構(gòu)的應(yīng)該知道。與散列表相關(guān)的一個(gè)概念叫做可散列,什么是可散列?在python官方定義中是這樣說(shuō)的:...

    toddmark 評(píng)論0 收藏0
  • Python奇遇:特殊方法窺探

    摘要:在中,特殊方法以雙下劃線開(kāi)始,以雙下劃線結(jié)束。真假值,如果向量模為,返回實(shí)現(xiàn)向量加法實(shí)現(xiàn)向量乘法,例如返回向量的模返回歐幾里德范數(shù)找個(gè)例子運(yùn)行下。怎么辦中有個(gè)特殊方法,可以修改控制臺(tái)輸出的樣式。 什么是特殊方法?當(dāng)我們?cè)谠O(shè)計(jì)一個(gè)類的時(shí)候,python中有一個(gè)用于初始化的方法$__init__$,類似于java中的構(gòu)造器,這個(gè)就是特殊方法,也叫作魔術(shù)方法。簡(jiǎn)單來(lái)說(shuō),特殊方法可以給你設(shè)計(jì)的...

    niceforbear 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<