1. 程式人生 > >Python優秀開源專案Rich原始碼解析

Python優秀開源專案Rich原始碼解析

這篇文章對優秀的開源專案Rich的原始碼進行解析,OMG,盤他。為什麼建議閱讀原始碼,有兩個原因,第一,單純學語言很難在實踐中靈活應用,通過閱讀原始碼可以看到每個知識點的運用場景,印象會更深,以後寫程式碼的時候就能應用起來;第二,通過閱讀優秀的開原始碼,可以學習比人的程式碼規範、設計思路;第三,參與到開源社群,獲得更廣闊的的發展前景;第四,面試加分項。所以,有時間的話還是建議大家多讀讀優秀開源專案的原始碼。

下面進入今天的主題,這個開源專案的名字叫Rich,地址:https://github.com/willmcgugan/rich (可以點選文末閱讀原文檢視)。 這個專案是個英國老鐵開發的,比較友好的是有中文文件。它的作用是可以在控制檯輸出富文字和精美的視覺化格式(如:表格、進度條和markdown)。截圖感受一下

各種格式
![](https://user-gold-cdn.xitu.io/2020/7/5/1731f4c8d63527f7?w=1710&h=302&f=gif&s=3389749)
進度條

效果看起來很酷炫,我忍不住看了一些程式碼,發現作者用的是Python 3.8版本實現的,好多新特性我也不瞭解,所以在看原始碼過程中還補了一下語法基礎。下面以一個例子來簡單看看Rich的原始碼,原始碼的講解我儘量言簡意賅,重點講解原始碼中涉及的一些關鍵的知識點。

先撿個軟柿子捏,如下:

from rich import print

print('Hello, [bold yellow]World[/bold yellow]!')

輸出效果:

可以看到對單詞World顯示為粗體、紅顏色。

先通過一張圖來看看大致流程

簡單來說就是將文字的格式轉化成標準輸出能夠識別的格式,然後輸出即可。下面來講解原始碼,當我們呼叫print函式時,最終程式會跳轉到console.py檔案的print函式中,執行以下程式碼

呼叫self._collect_renderables函式處理輸入的字串,將需要格式化的部分標出來,返回的renderables變數是一個Text列表,因為輸入只有1個字串,所以列表的大小為1,變數結果如下

Span(7, 12, 'bold red')便是框出來需要格式化的內容。

上述程式碼還有一個with self

,它的作用我們一會兒再說。接著print函式往下看

這裡會遍歷剛剛提到的renderables變數,先呼叫render函式渲染輸入的文字,然後呼叫extend函式將render返回的結果新增到self._buffer列表裡。這裡有幾個知識點簡單說一下

  • self._buffer是函式呼叫,由於它加了@property註解,所以呼叫是可以不用加小括號,它返回的是self._thread_locals.buffer變數,該變數是List[Segment]型別的
  • self._thread_locals.buffer變數用到dataclasses模組的field函式初始化,初始化程式碼為buffer: List[Segment] = field(default_factory=list)dataclassesPython 3.7 版本的新引入的模組,field函式可提供更加靈活的初始化方式,並且該模組中的@dataclass註解可以為類自動新增__init__等方法,比較方便
  • extend = self._buffer.extend這種寫法將listextent函式存到了臨時變數裡,後續直接通過extend呼叫該函式,比物件名.extend的方式更簡潔。

下面我們來看render(renderable, render_options)函式的渲染邏輯,該函式裡會呼叫下面的程式碼

render_iterable = renderable.__rich_console__(self, options)

在函式聲明裡renderable物件是RenderableType型別的,但實際上Text型別的,並且這兩種型別沒有繼承關係,這裡沒太想明白作者為什麼這樣搞。所以,這裡的__rich_console__函式我們要到text.py檔案中去找。__rich_console__函式最終會呼叫Text物件的render函式,核心程式碼如下:

def render(self, console: "Console", end: str = "") -> Iterable["Segment"]:
  style_map = {index: get_style(span.style) for index, span in enumerated_spans}

  _Segment = Segment

  for (offset, leaving, style_id), (next_offset, _, _) in zip(spans, spans[1:]):
    yield _Segment(text[offset:next_offset], get_current_style())

呼叫get_style函式,將格式轉為Style物件,如:'bold red'轉成Style物件,然後按照不同的顯示格式進行‘分片’,每個‘片段’構造一個Segment物件儲存文字及其對應的格式。

get_style函式會呼叫Style.parse(name)生成Style物件,核心程式碼如下

@lru_cache(maxsize=1024)
def parse(cls, style_definition: str) -> "Style":
  words = iter(style_definition.split())
  for original_word in words:
    word = original_word.lower()
    if word == "on":
      # ...省略
    elif word in style_attributes:
      attributes[style_attributes[word]] = True
    else:
      color = word
  style = Style(color=color, bgcolor=bgcolor, link=link, **attributes)
  return style

引數style_definition取值為bold red,分割後生成['bold', 'red']列表,當word變數等於'bold'時,會執行attributes[style_attributes[word]] = True語句,執行後attributes等於{'bold': true},它是一個字典。當word變數等於red時,執行color=word語句。最終呼叫導數第二行構造Style物件,Style物件最核心的兩個資料形式_attributes_color, 前者是int型別,在我們例子中取值是1,代表'bold',即:粗體。後者代表顏色,即:'red',它是Color型別的,該類中有個屬性number也是我們後續要用到的。

下面來看下__rich_console__函式返回了哪些Segment物件

可以看到有4個,每一個都有文字及其Style物件。

回到render(renderable, render_options)函式,剛剛介紹了__rich_console__部分,下面還有返回的程式碼, 一起來看看

iter_render = iter(render_iterable)
for render_output in iter_render:
  if isinstance(render_output, Segment):
    yield render_output

render_iterable變數是__rich_console__的返回值,即:4個Segment物件。遍歷後通過yield方式返回。該關鍵字用來返回一個迭代器,也可以理解為一個列表。並且yield返回有個特點,函式返回值只有真正被使用的時候才會執行呼叫函式。

這樣,render(renderable, render_options)函式就講解完了,返回上一層extend(render(renderable, render_options)),通過extend函式將4個Segment物件儲存到buffer中,結果如下

然後print方法就執行完了。看起來已經結束了,然而控制檯列印的程式碼貌似沒有看到。答案就在剛剛的with self中,with關鍵字使得執行完程式碼體後,會自動呼叫self__exit__函式。__exit__函式中呼叫_render_buffer函式進行最終的輸出,核心程式碼如下

output: List[str] = []
append = output.append
for line in Segment.split_and_crop_lines(buffer, self.width, pad=False):
    for text, style, is_control in line:
        if style and not is_control:
            append(
                style.render(
                    text,
                    color_system=color_system,
                    legacy_windows=legacy_windows,
                )
            )
rendered = "".join(output)

return rendered

split_and_crop_lines函式是為了適應控制檯的寬度,暫時忽略它。line變數仍然是剛剛提到的4個Segment物件,通過for text, style, is_control in line直接將每個Segment物件的屬性解出來並賦給text, style, is_control變數,最終每個style物件都會呼叫render方法完成最後的渲染。

render方法核心程式碼如下

attrs = self._make_ansi_codes(color_system)
rendered = f"\x1b[{attrs}m{text}\x1b[0m" if attrs else text

_make_ansi_codes函式就不展開了, 其實就是利用上面提到的_attributesnumber屬性生成標準輸出的能夠識別的格式,返回值attrs的結果為1;31,1取自_attributes代表粗體,31中的1取自number代表顏色,其他顏色取值是不同的,比如黃色是33,紫色是35。最後通過f-string格式(新特性)生成rendered變數,取值為World 它就是標準輸出流能夠識別的格式。

回到_render_buffer函式中,呼叫rendered = "".join(output)將4個渲染後的片段拼在一起,返回。返回後執行的程式碼如下:

text = self._render_buffer()
if text:
    self.file.write(text)

self.file變數的賦值語句為self.file = file or sys.stdout,由於我們沒有定義file變數,所以self.file取值為sys.stdout。最終的輸出為sys.stdout.write(text),至此整個流程就講解完了。如果你理解了上述邏輯,應該可以通過下面程式碼輸出同樣的效果

sys.stdout.write('Hello, \033[1;31mWorld\033[0m!')

所以Rich做的就是把文字格式準成標準輸出流能識別的格式。

Rich裡用到的程式碼確實挺新的,能學到很多東西,比直接看書來的快,有興趣的朋友可以自行閱讀。歡迎關注公眾號**渡碼**不斷分享優秀開源專案原始碼分析

相關推薦

Python優秀開源專案Rich原始碼解析

這篇文章對優秀的開源專案Rich的原始碼進行解析,OMG,盤他。為什麼建議閱讀原始碼,有兩個原因,第一,單純學語言很難在實踐中靈活應用,通過閱讀原始碼可以看到每個知識點的運用場景,印象會更深,以後寫程式碼的時候就能應用起來;第二,通過閱讀優秀的開原始碼,可以學習比人的程式碼規範、設計思路;第三,參與到開源社群

Android熱更新開源專案Tinker原始碼解析系列之一:Dex熱更新

Tinker是微信的第一個開源專案,主要用於安卓應用bug的熱修復和功能的迭代。 Tinker github地址:https://github.com/Tencent/tinker 首先向微信致敬,感謝毫無保留的開源出了這麼一款優秀的熱更新專案。

安卓實用優秀開源專案

沉浸式狀態列和沉浸式導航欄 參考資料:https://www.jianshu.com/p/2a884e211a62 開源地址 安卓常用工具類 參考資料: https://blankj.com/2016/07/31/android-utils-code/ 開源地址 動態許可

android面試——開源框架的原始碼解析

1、EventBus (1)通過註解+反射來進行方法的獲取 註解的使用:@Retention(RetentionPolicy.RUNTIME)表示此註解在執行期可知,否則使用CLASS或者SOURCE在執行期間會被丟棄。 通過反射來獲取類和方法:因為對映關係實際上是類對映到所有此類

【機器人學】機器人開源專案KDL原始碼學習:(8)KDL的精髓

  首先說一下我的心得: 1. 我認為KDL的精髓是Spatial Vector,結合C++等面向物件的語言可以寫出較好的軟體。 2. 直接閱讀KDL程式碼不適合初學者學習機械臂動力學。 3. 要學習機械臂動力學的話應首先閱讀使用3維向量推導公式的文獻,也就是線速度和角速度獨立分析

【機器人學】機器人開源專案KDL原始碼學習:(4)機械臂逆動力學的牛頓尤拉演算法

  機械臂的逆動力學問題可以認為是:已知機械臂各個連桿的關節的運動(關節位移、關節速度和關節加速度),求產生這個加速度響應所需要的力/力矩。KDL提供了兩個求解逆動力學的求解器,其中一個是牛頓尤拉法,這個方法是最簡單和高效的方法。    牛頓尤拉法演算法可以分為三個步驟: step1:

【機器人學】機器人開源專案KDL原始碼學習:(6)笛卡爾空間軌跡規劃、圓弧過渡、姿態插值、梯形速度、pathlength

  本文的內容是對另一篇文章(連結)的補充,對Trajectory_example.cpp涉及到的原理作一些簡單的講解,主要內容是:   (1)機器人路徑規劃圓弧過渡的原理;   (2)機器人路徑規劃梯形波的原理;   (3)機器人末端姿態插值的方法(角-軸);   (4)KDL

【機器人學】機器人開源專案KDL原始碼學習:(7)examples中的CMakeList.txt檔案解讀

通過學習KDL開源專案的程式碼可以學習CMake構建程式的知識,現簡單介紹一下orocos_kinematics_dynamics-master\orocos_kinematics_dynamics-master\orocos_kdl\examples\CMakeList.txt檔案的指令。

【機器人學】機器人開源專案KDL原始碼學習:(5)KDL如何求解幾何雅克比矩陣

這篇文章試圖說清楚兩件事:1. 幾何雅克比矩陣的本質;2. KDL如何求解機械臂的幾何雅克比矩陣。 一、幾何雅克比矩陣的本質 機械臂的關節空間的速度可以對映到執行器末端在操作空間的速度,這種對映可以通過一個矩陣來描述,就是幾何雅克比矩陣,瞭解雅克比矩陣需要了解這種對映關係的本質,這

【機器人學】機器人開源專案KDL原始碼學習:(3)機器人操作空間路徑規劃(Path Planning)和軌跡規劃(Trajectory Planning)示例

很多同學會把路徑規劃(Path Planning)和軌跡規劃(Trajectory Planning)這兩個概念混淆,路徑規劃只是表示了機械臂末端在操作空間中的幾何資訊,比如從工作臺的一端(A點)沿直線移動到另一端(B點)。而軌跡規劃則加上了時間律,比如它要完成的任務是從A點開始到B點結束,中間

優秀開源庫SDWebImage原始碼淺析

世人都說閱讀原始碼對於功力的提升是十分顯著的, 但是很多的著名開源框架原始碼動輒上萬行, 複雜度實在太高, 這裡只做基礎的分析。 簡潔的介面 首先來介紹一下這個 SDWebImage 這個著名開源框架吧, 這個開源框架的主要作用就是: Asynchronous image downloader w

【轉】如何閱讀大型前端開源專案原始碼

目前網上有很多「XX原始碼分析」這樣的文章,不過這些文章分析原始碼的範圍有限,有時候講的內容不是讀者最關心的。同時我也注意到,原始碼是在不斷更新的,文章裡寫的原始碼往往已經過時了。因為這些問題,很多同學都喜歡自己看原始碼,自己動手,豐衣足食。 這篇文章主要講的是閱讀大型的前

Vue-cli 命令列工具專案配置原始碼解析

我們都知道vue cli是vue.js 提供一個官方搭建專案命令列工具,可用於快速搭建大型單頁應用。以下是Vue-cli @2.9搭建專案的配置原始碼解析 相關目錄 ├── build │ ├── build.js │ ├── utils.js │ ├── check-versions

向大神看齊: 如何閱讀大型前端開源專案原始碼(轉)

作者簡介 Daniel 螞蟻金服·資料體驗技術團隊轉自: https://github.com/ProtoTeam/blog/blob/master/201805/3.md目前網上有很多「XX原始碼分析」這樣的文章,不過這些文章分析原始碼的範圍有限,有時候講的內容不是讀者最關

跳一跳python輔助軟體思路及原始碼解析

跳一跳python輔助軟體思路及影象識別原始碼解析 本文將梳理github上最火的wechat_jump_game的實現思路,並解析其影象處理部分原始碼 首先廢話少說先看效果 核心思想 獲取棋子到下一個方塊的中心點的距離 計算觸控式螢幕幕的時間

《Linux高效能伺服器》附帶專案springsnil原始碼解析

原始碼地址 安裝及使用 下載原始碼: git clone https://github.com/liu-jianhao/springsnail.git 然後進入springsnil目錄直接make即可生成可執行檔案 填寫配置檔案,我測試的是網站是網易雲音樂,首先我先看看網

推薦|23個Python爬蟲開源專案程式碼:爬取微信、淘寶、豆瓣、知乎、微博等

今天為大家整理了23個Python爬蟲專案。整理的原因是,爬蟲入門簡單快速,也非常適合新入門的小夥伴培養信心。所有連結指向GitHub,祝大家玩的愉快 1、WechatSogou [1]– 微信公眾號爬蟲。 基於搜狗微信搜尋的微信公眾號爬蟲介面,可以擴充套件成基於搜狗搜尋的爬

關於閱讀開源專案原始碼,有哪些經驗值得分享?

1、有耐心,忌吃熱豆腐。 有些特別勤快的人,總以為自己能很快閱讀完原始碼,他們之中的有些用量來衡量自己的能幹程度,於是只抓量不抓質。 但是程式碼中涉及到的很多是思想,慢慢領悟其中的精華,這是需要花時間去消化的。 閱讀開源專案的原始碼時,要有耐心,不要浮

Android Studio匯入github優秀開源專案SlidingMenu(簡單方法)

在app 的build.gradel裡面新增 repositories { maven { url "http://jzaccone.github.io/SlidingMenu-aar" } ... } dependencies { compile 'c

GitHub上排名前100的優秀開源專案

主要對當前 GitHub 排名前 100 的專案做一個簡單的簡介, 方便初學者快速瞭解到當前 Objective-C 在 GitHub 的情況. 若有任何疑問可通過微博@甲骨文兒聯絡我 專案名稱 專案資訊 作者是 NSHipster 的博主, iOS 開發界的大神級人