python基礎教程筆記—即時標記(詳解)


  最近一直在學習python,語法部分差不多看完了,想寫一寫python基礎教程后面的第一個項目。因為我在網上看到的別人的博客講解都並不是特別詳細,僅僅是貼一下代碼,書上內容照搬一下,對於當時剛學習python的我幫助有限。

  下面是自己學習過程整理的一些內容。

  

基礎版:

  基礎教程上面的項目例子,都會先出一個基礎的代碼版本,然后根據第一個版本,進行相應的補充完善。我們先來看一下util.py這個文件。

 1 #encoding:utf-8
 2 #生成器,for循環時會依次返回每一行,它只在文件的最后追加了一個空行\n
 3 def lines(file):
 4     for line in file:yield line
 5     yield '\n'
 6 #生成器,for循環時會依次返回文本塊組成的函數
 7 def blocks(file):
 8     block = []
 9     for line in lines(file):
10         if line.strip():
11             block.append(line)
12         elif block:
13             yield ''.join(block).strip()
14             block = []

這里的兩個主要內容是生成器和for...in語法。

首先我們來看lines()方法,參數是文件,然后對文件進行循環,每次讀取一行文件,主意這離的yield關鍵字,這里代表方法是一個生成器,循環的時候到yield我們可以理解成返回一次line內容。文本讀完后,yield處是一個'\n'。

blocks()方法就使用了上面的生成器,每次循環取出內容后,對line內容進行判斷,如果有值,去除兩邊空格,添加到列表中,否則將block列表生成字符串。我們可以看出blocks也是一個生成器,他的實際功能是,從文件中,依次讀取出來一個文本塊。

 

  然后是simple_markup.py文件。

 1 import sys,re
 2 from util import *
 3 
 4 print '<html><head><title>hello</title></head><body>'
 5 
 6 title = True
 7 for block in blocks(sys.stdin):
 8     block = re.sub(r'\*(.+?)\*',r'<em>\1</em>',block)
 9     if title:
10         print '<h1>'
11         print block
12         print '</h1>'
13         title = False
14     else:
15         print '<p>'
16         print block
17         print '</p>'
18 print '</body></html>'
這里我們需要注意一下,re.sub(r'\*(.+?)\*',r'<em>\1</em>',block),他是re模塊的應用,首先增則匹配到內容,然后替換式替換。
其他部分,就是判斷title是否為True,若是則給h1標簽,否則給p標簽。

擴展版:
  在上面的文件中,功能基本實現,下面的內容是功能的復雜實現。我們接着往下看,首先是handlers.py文件。
  
 1 #encoding:utf-8
 2 class Handler:
 3     '調用方法的處理類'
 4 
 5     #判斷當前類是否有對應的方法,所有的話則根據提供的額外參數使用對應方法
 6     def callback(self,prefix,name,*args):
 7         method = getattr(self,prefix+name,None)
 8         if callable(method):return method(*args)
 9     
10     #callback的輔助方法,前綴就是start,只需要提供方法名即可
11     def start(self,name):
12         self.callback('start_',name)
13     #前綴為end的callback輔助方法
14     def end(self,name):
15         self.callback('end_',name)
16     
17     #返回方法名subsutitution    
18     def sub(self,name):
19         def substitution(match):
20             result = self.callback('sub_',name,match)
21             if result is None: result = match.group(0)
22             return result
23         return substitution
24 
25 class HTMLRenderer(Handler):
26     def start_document(self):
27         print '<html><head><title>title</title></head><body>'
28     def end_documrnt(self):    
29         print '</body></html>'
30     def start_paragraph(self):
31         print '<p>'
32     def end_paragraph(self):
33                 print '</p>'
34     def start_heading(self):
35                 print '<h2>'
36     def end_heading(self):
37                 print '</h2>'
38     def start_list(self):
39                 print '<ul>'
40     def end_list(self):
41                 print '</ul>'
42         def start_listitem(self):
43                 print '<li>'
44         def end_listitem(self):
45                 print '</li>'
46         def start_title(self):
47                 print '<h1>'
48         def end_title(self):
49                 print '</h1>'
50     def sub_emphasis(self,match):
51         return '<em>%s</em>' % match.group(1)
52     def sub_url(self,match):
53         return '<a href="%s">%s</a>' % (match.group(1),match.group(1))
54     def sub_mail(self,match):
55         return '<a href="mailto:%s">%s</a>' % (match.group(1),match.group(1))
56     def feed(self,data):
57         print data

先看Handler類,他有四個方法,其中重點是callback和sub。

callback:兩個必須參數,一個額外參數。

  getAttr()用來判斷類中是否存在prefix+name的方法,若存在返回prefix+name,否則返回None。

  callable()用來判斷方法是否可以調用,若可以調用,則給予參數*args並且調用,*args的含義是額外參數。

start,end是包裝了callback的兩個方法,不細表。

sub:

  目的是返回一個函數作為re.sub的替換函數,這樣re.sub就不是寫死的了。其中定義了一個substitution方法,實際上調用后返回的就是這個方法。他也就是我們后面re.sub中需要用到的替換函數。

  細心的朋友可能會注意到,這里有一個match參數,當時在這里我費解了很久,明明沒有這個參數,可是之后的調用卻確實使用到了,我打印這個參數,顯示的是re對象。

  書上有這樣一個小例子,

1 from handlers import *
2 handler = HTMLRenderer()
3 import re
4 print re.sub(r'\*(.+?)\*',handler.sub('emphasis'),'this *is* a test ')
5 #輸出為'This <em>is</em> a test'

     當時在這了我完全就懵逼了,因為handler.sub('emphasis')返回的明明是一個方法,但是他沒有match參數啊。

  然后仔細看書,書上在前面有這樣一句話,re.sub函數可以將第一個函數作為第二個參數。至少筆者覺得這句話寫的很奇怪,’第一個函數‘明明要寫成第一個參數啊有木有。好吧,不吐槽這些。

  大概意思就是,re.sub的第二個參數可以是一個函數作為替換式,替換式的參數就是re.sub的第一個參數匹配后返回的正則對象。

  這下就可以看懂了,我們會去調用sub_emphasis(self,match),然后match.group(1)表示的實際上是is。關於group(1)大家去看一下,re模塊的內容,在這里我就直接告訴你他的內容,就是匹配式(.+?)中的內容。

  HTMLRenderer類繼承了Handler類,其中主要定義了一些用來輸出的方法,不細說。

  

  再來看rules.py文件。

 1 #encoding:utf-8
 2 class Rule:
 3     def action(self,block,handler):
 4         handler.start(self.type)
 5         handler.feed(block)
 6         handler.end(self.type)
 7         return True
 8 
 9 class HeadingRule(Rule):
10     type = 'heading'
11     #不包含\n,也就是說並非最后一個塊;長度小於70;不以冒號結尾
12     def condition(self,block):
13         return not '\n' in block and len(block) <=70 and not block[-1] == ':'
14 
15 class TitleRule(HeadingRule):
16     type = 'title'
17     #只工作一次,處理第一個快,因為處理完一次之后first的值被設置為了False,所以不會再執行處理方法了
18     first = True
19     def condition(self,block):
20         if not self.first: return False
21         self.first = False
22         return HeadingRule.condition(self,block)
23 
24 class ListItemRule(Rule):
25     type = 'listitem'
26     def condition(self,block):
27         return block[0] == '-'
28     def action(self,block,handler):
29         handler.start(self.type)
30         handler.feed(block[1:].strip())
31         handler.end(self.type)
32         return True
33 
34 class ListRule(ListItemRule):
35     type = 'list'
36     inside = False
37     def condition(self,block):
38         return True
39     def action(self,block,handler):
40         if not self.inside and ListItemRule.condition(self,block):
41             handler.start(self.type)
42             self.inside = True
43         elif self.inside and not ListItemRule.condition(self,block):
44             handler.end(self.type)
45             self.inside = False
46         return False
47 
48 class ParagraphRule(Rule):
49     type = 'paragraph'
50     def condition(self,block):
51         return True

   這里比較簡單,我們先看看基類Rule,定義了兩個方法,condition和action.

  condition接受一個文本塊作為參數,通過返回布爾值來表示文本塊是否適合當前的規則。

  action接受文本塊和處理程序對象作為參數,用來對文本塊執行操作,進行輸出。

  集成的類都不復雜,這里單獨說一下ListRule。

  這里定義了一個變量inside為True,我們可以理解這個變量的意思是—List列表開始。因為在html中List中還會包含節點,也就是這里的ListItem,所以他會在遇到一個列表項的時候觸發一次,然后在最后一個列表項的時候再次觸發。所以inside作為一個標志位,用來進行判斷符合規則的文本塊時需要執行start還是end方法。

 

  最后一個文件,markup.py

 1 #encoding:utf-8
 2 import sys,re
 3 from handlers import *
 4 from util import *
 5 from rules import *
 6 
 7 class Parser:
 8     #初始化一些屬性
 9     def __init__(self,handler):
10         self.handler = handler
11         self.rules = []
12         self.filters = []
13     #向規則列表中添加規則
14     def addRule(self,rule):
15         self.rules.append(rule)
16     #向過濾器列表中添加過濾器
17     def addFilter(self,pattern,name):
18         #創建過濾器,實際上這里return的是一個替換式
19         def filter(block,handler):
20             return re.sub(pattern,handler.sub(name),block)
21         self.filters.append(filter)
22     #對文件進行處理
23     def parse(self,file):
24         self.handler.start('document')
25         #對文件中的文本塊依次執行過濾器和規則    
26         for block in blocks(file):
27             for filter in self.filters:
28                 block = filter(block,self.handler)
29             for rule in self.rules:
30                 #判斷文本塊是否符合相應規則,若符合做執行規則對應的處理方法
31                 if rule.condition(block):
32                     last = rule.action(block,self.handler)
33                     if last:break
34         self.handler.end('document')
35 
36 class BasicTextParser(Parser):
37     def __init__(self,handler):
38         Parser.__init__(self,handler)
39         self.addRule(ListRule())
40         self.addRule(ListItemRule())
41         self.addRule(TitleRule())
42         self.addRule(HeadingRule())
43         self.addRule(ParagraphRule())
44 
45         self.addFilter(r'\*(.+?)\*','emphasis')
46         self.addFilter(r'(http://[\.a-zA-Z/]+)','url')
47         self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[a-zA-Z]+)','mail')
48         
49 handler = HTMLRenderer()
50 parser = BasicTextParser(handler)
51 
52 parser.parse(sys.stdin)
53         

  同樣先看基類Parser,構造函數需要一個handler對象作為參數,以供全局調用,同時初始化了兩個列表。

  addRule和addFilter的目的是向規則和過濾器列表添加元素。

  parse方法,讀取文本文件,循環出每一個文本塊,先通過過濾器過濾,然后執行相應規則。

  我們注意,規則和按照列表依次執行的,他會判斷返回值,若為False則不再對文本塊執行后續規則了。

  BasicTextParser類,的構造函數只是在基類的基礎上增加了,向規則和過濾器列表添加具體內容的步驟。

  

  然后初始化類,並且對文件執行parse方法,即時標記項目完成。

 

后記:

  學習完python語法后,真正動手做的第一個項目,對初學者還是有一點難度的,尤其是整體細節上。

  后續會依次寫完其他幾個項目的學習筆記。歡迎喜歡的朋友關注,bye!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM