編寫遞迴下降語法分析器 裝飾器詳解

2021-10-12 04:37:38 字數 4291 閱讀 9915

裝飾器是python中很重要的乙個知識,但是對於多數新手來說,裝飾器有點難以理解,所以看到裝飾器就想繞道而走,但是這會給自己留下乙個很大的隱患,一旦面試官要你實現乙個簡單的裝飾器的時候,你就會因為這個坑而錯失機會。

那麼我們為什麼要使用裝飾器呢?因為程式編寫講究乙個ocp原則,也就是開閉原則,拒絕已完成功能的**的修改,推薦**功能的拓展,所以這也意味著,我們不能夠再去修改已經完成的功能,而是要去擴充套件他的功能,因此,我們需要使用到裝飾器來做這個擴充套件功能。我們會圍繞建立普通裝飾器、建立通用裝飾器、多個裝飾器的裝飾、攜帶引數的裝飾器及裝飾遞迴函式來開展話題。

首先裝飾器的建立需要滿足什麼條件?裝飾器本質上是乙個閉包,所以它也符合閉包的三個特性:

import time

def fun_out(num):

def fun_inner():

begin = time.time()

print(num)

end = time.time()

print('程式執行時間為%s' % (end - begin))

return fun_inner

那麼現在這個高階函式就是乙個閉包函式了。

那麼既然有裝飾器的使用,我們當然也需要乙個需要被裝飾的目標函式了。

def fun():

for i in range(200000):

print(i)

現在準備工具已經完成,那麼我們怎麼讓上面的閉包函式成為乙個具有計算程式執行時間的裝飾器呢?

import time

def fun_out(fun):

def fun_inner():

begin = time.time()

res = fun()

end = time.time()

print('程式執行時間為%s' % (end - begin))

return fun_inner

現在我們的這個函式就成為了乙個裝飾器函式。我們來呼叫程式看看這個結果。
import time

def fun_out(fun):

def fun_inner():

begin = time.time()

fun()

end = time.time()

print('程式執行時間為%s' % (end - begin))

return fun_inner

def fun():

for i in range(200000):

print(i)

f = fun_out(fun)

f()執行結果:0.

..199999

程式執行時間為0.6352810859680176

那麼這就是我們的執行結果了,可以看到我們完成了程式的新增,但是並沒有對程式本身功能做更改。但是這麼寫不是太友好,如果說使用人員使用這個程式,步驟的增多會降低體驗度,同時,這樣也會把開發人員的目光吸引到如何呼叫裝飾器上,而不是針對目標函式做裝飾,所以我們一般是使用語法糖的寫法。
import time

def fun_out(fun):

def fun_inner():

begin = time.time()

fun()

end = time.time()

print('程式執行時間為%s' % (end - begin))

return fun_inner

@fun_out

def fun():

for i in range(200000):

print(i)

fun()

執行結果:0.

..199999

程式執行時間為0.6352810859680176

這樣的使用是更加的便捷的。通用裝飾器,即被裝飾的函式是否攜帶引數都可以使用,則是通用裝飾器。
import time

def fun_out(fun):

def fun_inner(*args, **kwargs):

begin = time.time()

res = fun(*args, **kwargs)

end = time.time()

print('程式執行時間為%s' % (end - begin))

return res

return fun_inner

@fun_out

def fun1(a, b):

time.sleep(1)

return a + b

r = fun1(1, 2)

print(r)

執行結果:

程式執行時間為1.0004768371582031

3

我們給內部函式新增不定長引數,不管目標函式是否攜帶引數都是可以直接使用此裝飾器。大家都知道了使用乙個裝飾器,我們的目標函式是沒有問題的,那麼如果我們使用多個裝飾器是怎麼樣的呢?

那麼大家有沒有注意到程式的執行順序問題,裝飾器的執行順序是從下往上執行的,並不是我們想象中的從上往下執行,這個是多個裝飾器同時裝飾乙個目標函式的特殊性質,哪個裝飾器離目標函式近,那麼那個裝飾器就先執行。攜帶引數的裝飾器是乙個比較特殊的裝飾器了,當我們使用的裝飾器是攜帶了引數的時候,我們必須在裝飾器的外層在包裹一層函式才可以。

def logging(sign):

def fun_out(fun):

def fun_inner(num1, num2):

if sign == "+":

print("正在呼叫加法運算")

elif sign == "-":

print("正在呼叫減法運算")

res = fun(num1, num2)

return res

return fun_inner

return fun_out

@logging("+")

def add(a, b):

result = a + b

return result

@logging("-")

def sub(a, b):

result = a - b

return result

res = add(1, 2)

print(res)

res = sub(1, 2)

print(res)

其實也是同樣的道理,既然裝飾器的外層函式只能接受目標函式物件這乙個引數,那麼我們就必要要用另外乙個函式去接受這個引數才可以,所以直接用一層函式來包裹裝飾器就可以了,當然我們在最後要返回裝飾器的外層函式物件。遞迴函式的裝飾是乙個比較特殊的現象,裝飾器有個機制,當它發現此函式已經被裝飾了之後,那麼它就不會在裝飾這個函式,而是會直接停止裝飾程式,歸其原因還是因為裝飾器的記憶體原理。    裝飾器裝飾乙個函式,會把目標函式的物件儲存到開闢的記憶體空間中,所以當記憶體空間已經存在了這個被裝飾的目標函式的物件的時候,那麼程式會直接報錯。所以我們不能夠直接使用裝飾器裝飾遞迴函式,而是要使用乙個外層函式先包裹住遞迴函式,讓閉包函式在此函式中執行完了之後再結束執行,這個時候,裝飾器中就不會出現重複的函式物件了。
import time

def fun_out(fun):

def fun_inner(*args, **kwargs):

begin = time.time()

res = fun(*args, **kwargs)

end = time.time()

print('程式執行時間為%s' % (end - begin))

return res

return fun_inner

@fun_out

# 求n的m次冪

def decorator(n, m):

def fun(n, m):

time.sleep(1)

if m == 1:

return n

return n * fun(n, m - 1)

return fun(n, m)

res = decorator(10, 5)

print(res)

執行結果:

程式執行時間為5.0015575885772705

100000

以這樣的方式,我們就可以完成對遞迴函式的裝飾了。你學廢了嗎?

C 視覺化 遞迴下降語法分析器

演算法基本思想 消除了左遞迴和左公共因子的文法,每個產生式左邊的非終結符對應乙個函式,在子程式中實現對該非終結符所在產生是的右部語法成分的識別,分析過程是按產生式規則自頂向下一層一層呼叫相關子程式來完成的。具體實現過程是掃瞄原始檔,先做語法分析,建立各種 存放單詞 然後進行語法分析,遇到終結符進行匹...

LR 語法分析器

lr語法分析器算是基本完成了,只需要乙個文法定義檔案 syntax 就可以進行對應語言的語法分析,最後形成語法樹。詞法分析是固定的,採用c 的詞法定義。以後將加入動態的詞法分析。壓縮包中檔案的描述 lrtable.exe 是用文法定義檔案 syntax檔案 生成lr動作表檔案 action檔案 使用...

LALR語法分析器

lalr分析器 是一種規範lr分析方法的簡化形式。它可以對上下無關文法進行語法分析。lalr即 l ook ahead lr 其中,look ahead為 向前看 l代表對輸入進行從左到右的檢查,r代表反向構造出最右推導序列。lalr分析器 可以根據一種程式語言的正式語法的 產生式而對一段文字程式輸...