想學習python已經很久了,以前使用ArcGIS的時候學習過一些簡單的python語法,用來進行一些簡單的GIS數據處理,但是后來並沒有用到工作中也就荒廢了,后來斷斷續續看過一些,最近想學習一門新的語言,就拿python下手了,仔細考(拼)慮(湊)出以下理由(喜歡就是喜歡,不用理由):
- python強大庫支持,在科學計算、人工智能等高精尖的領域使用甚廣
- 在運維中也算是除shell外的第二語言吧
- 學習滲透測試的時候很多poc都是使用python寫的,為了不僅僅停留在使用工具上,進一步學習
- 在web中python也是快速開發的利器
本次學習主要是《python基礎教程》第二版,簡單回顧了前面幾章基礎知識后,開始拿后面的練習來學習,雖然代碼大部分是直接參考書中源碼,但是“紙上得來終覺淺,絕知此事要躬行”可不是廢話,學習這件事不動手就是耍流氓,在自己動手編寫書中例子的時候對python的語法和思想理解逐漸加深。先來一發
為了避免直接上代碼略顯突兀,顯寫出這個練習項目的思路:
先撇開具體實現,想想如果要實現將文本轉化位html的做法:
- 文本一定時滿足一定規則的
- 讀取文本內容
- 根據預定義的規則分析讀入的文本內容
- 符合某一種規則的文本加上對應的html標簽
- 繼續讀入文本,回到2繼續處理
再回來書中的項目,本項目共包含四個模塊
- util.py:負責對文本進行一定的格式化,並讀取文本
- rule.py:定義什么樣的文本可以轉化為什么樣的html,兩件事兒:
- 定義文本和html對應的規則
- 決定規則對應的處理程序(handler)
- handler.py:將給定規則的文本轉換為html
- parse.py:調用util.py讀取文本,調用rule.py判斷文本對應的規則,調用handler處理給定規則的文本
這樣子基本脈絡已經清晰了,看代碼
util.py

# -*- coding=utf-8 -*- def lines(file): ''' 給每個段落強制添加一個空行 ''' for line in file : yield line yield '\n' def blocks(file): ''' 將輸入的文件按按空行分塊 ''' block = [] for line in lines(file): if line.strip(): # line不是空行,屬於一個塊 block.append(line) elif block: # 當前行是空行,block不為空,塊已經結束,返回塊 yield ''.join(block).strip() block = []
handler.py

# -*- coding=utf-8 -*- class Handler: def callback(self, prefix, name, *args): method = getattr(self, prefix+name, None) if callable(method): return method(*args) def start(self, name): self.callback('start_', name) def end(self, name): self.callback('end_', name) def sub(self, name): def subtitution(match): result = self.callback('sub_', name, match) if result is None: match.group(0) return subtitution class HTMLRender(Handler): def start_document(self): print '<html><head><title>...</title></head><body>' def end_document(self): print '</body>' def start_paragraph(self): print '<p>' def end_paragraph(self): print '</p>' def start_head(self): print '<h2>' def end_head(self): print '</h2>' def start_title(self): print '<h1>' def end_title(self): print '</h1>' def start_list(self): print '<ul>' def end_list(self): print '</ul>' def start_listitem(self): print '<li>' def end_listitem(self): print '</li>' def sub_em(self, match): return '<em>%s</em>' % match.group(1) def sub_url(self, match): return '<a href="%s">%s</a>' % (match.group(1), match.group(1)) def sub_email(self, match): return '<a href="mailto:%s">%s</a>' % (match.group(1), match.group(1)) def feed(self, data): print data
rule.py

# -*- coding=utf-8 -*- class Rule: def action(self, block, handler): handler.start(self.type) handler.feed(block) handler.end(self.type) return True class HeadRule(Rule): ''' 小標題的規則,沒有換行符,長度小於70並且不以:結尾 ''' type = 'head' def condition(self, block): return not '\n' in block and len(block) < 70 and block[-1] != ':' class TitleRule(HeadRule): ''' 標題的規則,第一行是就是標題 ''' type = 'title' first = True def condition(self, block): if not self.first: return False self.first = False return HeadRule.condition(self, block) class ListItemRule(Rule): ''' li的規則,如果block以-開頭就是listitem,在顯示的時候去除- ''' type = 'listitem' def condition(self, block): return block[0] == '-' def action(self, block, handler): handler.start(self.type) handler.feed(block[1:].strip()) handler.end(self.type) return True class ListRule(ListItemRule): ''' ul的規則,第一個li開始之前,添加ul,在第一個不是li的時候結束ul ''' type = 'list' inside = False def condition(self, block): return True def action(self, block, handler): if not self.inside and ListItemRule.condition(self, block): handler.start(self.type) self.inside = True elif self.inside and not ListItemRule.condition(self, block): handler.end(self.type) # 每次返回False,在對block使用完本規則之后繼續使用其他規則 return False class ParagraphRule(Rule): ''' p段落規則,當前面所有的規則匹配的時候,默認使用本規則 ''' type = 'paragraph' def condition(self, block): return True
parse.py

# -*- coding=utf-8 import re, sys from util import * from handler import * from rule import * import ipdb class Parser: ''' 解析器,利用rule對文本進行解析並轉換為html,利用filter轉換塊內文本 ''' def __init__(self, handler): self.handler = handler self.rules = [] self.filters = [] def addRule(self, rule): self.rules.append(rule) def addFilter(self, pattern, name): def filter(block, handler): return re.sub(pattern, handler.sub(name), block) self.filters.append(filter) def parse(self, file): #pdb.set_trace() self.handler.start('document') for block in blocks(file): ipdb.set_trace() for filter in self.filters: block = filter(block, self.handler) for rule in self.rules: if rule.condition(block): last = rule.action(block, self.handler) if last: break self.handler.end('document') class BasicTextParser(Parser): ''' 基本文本解析器,只有h1,h2,p,ul,li,a,em,email ''' def __init__(self, handler): Parser.__init__(self, handler) self.addRule(ListRule()) self.addRule(ListItemRule()) self.addRule(TitleRule()) self.addRule(HeadRule()) self.addRule(ParagraphRule()) self.addFilter(r'\*(.+?)\*', 'em') self.addFilter(r'(http://[\.a-zA-Z]+)', 'url') self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[[a-zA-Z]+)', 'email') handler = HTMLRender() parser = BasicTextParser(handler) parser.parse(sys.stdin)
第一次練習就遇到這個項目顯得略大,不過搞清楚思路之后整體把我就沒問題了,在練習的過程中還遇到一些問題、學到一些東西,總結如下:
- 子類調用父類的方法(包括__init__)
父類名.方法名(self, 參數)
A類調用B類方法
b = B()
b.方法(參數)
- re模塊的sub方法
re.sub(regex_str, replace_str, str)
將str中符合正則表達式regex_str的子串用replace_str代替
如果replace_str是正則表達式,可以使用分組替換,例如
re.sub(r'\*(.+)\*', r'---\1---', "asdasdasd*nskjdfnkjdsnf*sdfsdf")
re.sub(r'\*(.+)\*',lambda m: "---" + m.group(1) + "---" ,"asdasdasd*nskjdfnkjdsnf*sdfsdf")
其實作者里面有兩個思想讓我觸動很深,甚是佩服:
- 一個模塊做最少的事,制作自己必須做的事
- 事件的路由分發(借由python發揮的淋漓盡致)
這兩種思想讓項目整體架構變的很清晰,對擴展也很友好,大寫的贊!
這個練習已經寫完幾天了,今天補上記錄一下,代碼位置
http://pan.baidu.com/s/1o7JVKKU