元編程python
1. 怎麼理解元編程
1.一種語言本來做不到的事情,通過你編程來修改它,使得它可以做到了,這就是元編程。
2.就是用代碼生成(操縱)代碼。
3.常見的開發語言均能做到元編程,Lisp這貨就不用多說了,C的Marco,C++的Template,Java的Annotation,C#的Attribute、Reflection、CodeDom和IL Emitter,各種腳本語言(如js、python)的eval,甚至連Unix/Linux的shell腳本也能。
4.元編程常見的應用場景很多,擴展(重構)語法、開發DSL、生成代碼、根據特定場景自動選擇代碼優化、解決一些正交的架構設計問題、AOP等等。
5.所以元編程存在的目的,就是多提供了一個抽象層次。
6.至於元編程有什麼優缺點,爭議還是比較大的。比如以重構語法的應用為例,很多元編程的反對者就認為這樣會導致代碼的可讀性、可維護性降低,分化社區,影響交流,因為每個開發人員都能搞一個自己的方言。
總的來說,元編程玩得轉的話會讓你日子好過一些,玩不轉會讓你日子不好過一些。
2. 如下代碼中違反了哪項編碼規范
如下代碼中違反了第四項編碼規范,代碼中是違反了第四項他的一個編碼規定的他的一個編碼規范的這一個第四項的話,他們內容是指的是不能夠在不開機的時候就對他的一個電腦進行一個隨便的一個鍵盤的一個按鍵。
3. python就業方向
python就業方向:python開發工程師、人工智慧工程師、大數據分析工程師、爬蟲開發工程師、搜索引擎工程師、游戲開發工程師、系統運維工程師。
Python具除了易讀易寫更兼具面向對象和函數式風格,還有不錯元編程能力已經成為IT運維、科學計算、數據處理等領域的主要編譯語言。
通過系統化的將各種管理工具結合,對各類工具進行二次開發,形成統一的伺服器管理系統。
4. 怎麼理解元編程
Meta- 這個前綴在希臘語中的本意是「在…後,越過…的」,類似於拉丁語的 post-,比如 metaphysics 就是「在物理學之後」,這個詞最開始指一些亞里士多德的著作,因為它們通常排序在《物理學》之後。
但西方哲學界在幾千年中漸漸賦予該詞綴一種全新的意義:關於某事自身的某事。比如 meta-knowledge 就是「關於知識本身的知識」,meta-data 就是「關於數據的數據」,meta-language 就是「關於語言的語言」,而 meta-programming 也是由此而來,是「關於編程的編程」。
弄清了詞源和字面意思,可知大陸將 meta- 這個前綴譯為「元」並不恰當。台灣譯為「後設」,稍微好一點點,但仍舊無法望文生義。也許「自相關」是個不錯的選擇,「自相關數據」、「自相關語言」、「自相關編程」——但是好像又太羅嗦了。
Anyway。先看看 meta-data:「我的電話是 +86 123 4567 8910」——這是一條數據;「+86 123 4567 8910 有十三個數字和一個字元,前兩位是國家代碼,後面是一個行動電話號碼」是關於前面那條數據的數據。那麼照貓畫虎,怎樣才算 meta-programming 呢?泛泛來說,只要是與編程相關的編程就算是 meta-programming 了——比如,若編程甲可以輸出 A - Z,那麼寫程序甲算「編程」;而程序乙可以生成程序甲(也許還會連帶著運行它輸出 A - Z),那麼編寫程序乙的活動,就可以算作 meta-programming,「元編程」。注意,程序甲和程序乙並不一定是同一種語言:
('A'..'Z').each do |char|
system("python -c 'print \"#{char}\"'")
end
如此說來,inline SQL 甚至動態生成 HTML 也是元編程了?摳定義的話是這樣吧。
不過 metaprogramming 更狹義的意思應該是指「編寫能改變語言語法特性或者運行時特性的程序」。換言之,一種語言本來做不到的事情,通過你編程來修改它,使得它可以做到了,這就是元編程。本版同文提及 method_missing,那麼 Wat — Destroy All Software Talks 之中給出了運行時元編程的經典範例:
>> ruby has no bare words
NameError: undefined local variable or method `words' for main:Object
from (irb) 1
>> def method_missing(*args); args.join(" "); end
=> nil
>> ruby has bare words
=> "ruby has bare words"
>> bare words can even have bangs!
=> "bare words can even have bangs!"
C、C++、Python、JavaScript…… 多數流行的語言或多或少都有元編程能力;Lisp 諸方言更是以元編程為基本。而 Ruby 更是因為元編程易用又強大,被許多人拿來寫 DSL,因為元編程可以捏出「本不存在的語法特性」來讓書寫 DSL 變得簡單。
5. Python 有什麼缺點
python的整個系統,我其實有非常多的不滿。但是用任何一門語言都是取捨問題,如果有一門語言,庫夠多,已讀,易用,性能高,我毫不猶豫立刻轉過去。python的強處在於龐大的庫,還有非常好的易讀和易用性。但是相比來說,性能一直是個問題。python的實現性能大約和C相差五倍上下。如果是大規模計算問題,大約能差10倍以上。當然,我們可以寫C擴展,但是這就不是使用python了。我們也可以說,很多時候我們不需要這么快的速度。這是個事實,但是不改變python性能差的事實。 python不但性能差,還有GIL這個玩意。以至於我現在對高並發計算都採取多進程的模式。多進程模式的通訊效率肯定比多線程低,而且麻煩。
另外,python在底層設計上,也表現出很強的實用主義傾向。這是比較外交術語的詞彙,更加直白的說法應當是,混亂,不知所謂。在閉包設計上採用free variable設計,而不是lisp中的environs設計。區別?你試試看在外層閉包中from lib import *。由於引入不定個數名稱,free variable無法處理。類似的問題還有LEGB規則,新手往往要花很長時間研究這個例子究竟是怎麼錯的: a = 1 def f(): print a a = 2 我勒個去,這種反直觀反人類的事情都有,還敢說自己易讀。
6. 如何用 Python 實現一個圖資料庫(Graph Database)
本文章是 重寫 500 Lines or Less 系列的其中一篇,目標是重寫 500 Lines or Less 系列的原有項目:Dagoba: an in-memory graph database。
Dagoba 是作者設計用來展示如何從零開始自己實現一個圖資料庫( Graph Database )。該名字似乎來源於作者喜歡的一個樂隊,另一個原因是它的前綴 DAG 也正好是有向無環圖 ( Directed Acyclic Graph ) 的縮寫。本文也沿用了該名稱。
圖是一種常見的數據結構,它將信息描述為若干獨立的節點( vertex ,為了和下文的邊更加對稱,本文中稱為 node ),以及把節點關聯起來的邊( edge )。我們熟悉的鏈表以及多種樹結構可以看作是符合特定規則的圖。圖在路徑選擇、推薦演算法以及神經網路等方面都是重要的核心數據結構。
既然圖的用途如此廣泛,一個重要的問題就是如何存儲它。如果在傳統的關系資料庫中存儲圖,很自然的做法就是為節點和邊各自創建一張表,並用外鍵把它們關聯起來。這樣的話,要查找某人所有的子女,就可以寫下類似下面的查詢:
還好,不算太復雜。但是如果要查找孫輩呢?那恐怕就要使用子查詢或者 CTE(Common Table Expression) 等特殊構造了。再往下想,曾孫輩又該怎麼查詢?孫媳婦呢?
這樣我們會意識到,SQL 作為查詢語言,它只是對二維數據表這種結構而設計的,用它去查詢圖的話非常笨拙,很快會變得極其復雜,也難以擴展。針對圖而言,我們希望有一種更為自然和直觀的查詢語法,類似這樣:
為了高效地存儲和查詢圖這種數據結構,圖資料庫( Graph Database )應運而生。因為和傳統的關系型資料庫存在極大的差異,所以它屬於新型資料庫也就是 NoSql 的一個分支(其他分支包括文檔資料庫、列資料庫等)。圖資料庫的主要代表包括 Neo4J 等。本文介紹的 Dagoba 則是具備圖資料庫核心功能、主要用於教學和演示的一個簡單的圖資料庫。
原文代碼是使用 JavaScript 編寫的,在定義調用介面時大量使用了原型( prototype )這種特有的語言構造。對於其他主流語言的用戶來說,原型的用法多少顯得有些別扭和不自然。
考慮到本系列其他資料庫示例大多是用 Python 實現的,本文也按照傳統,用 Python 重寫了原文的代碼。同樣延續之前的慣例,為了讓讀者更好地理解程序是如何逐步完善的,我們用迭代式的方法完成程序的各個組成部分。
原文在 500lines 系列的 Github 倉庫中只包含了實現代碼,並未包含測試。按照代碼注釋說明,測試程序位於作者的另一個代碼庫中,不過和 500lines 版本的實現似乎略有不同。
本文實現的代碼參考了原作者的測試內容,但跳過了北歐神話這個例子——我承認確實不熟悉這些神祇之間的親緣關系,相信中文背景的讀者們多數也未必了解,雖然作者很喜歡這個例子,想了想還是不要徒增困惑吧。因此本文在編寫測試用例時只參考了原文關於家族親屬的例子,放棄了神話相關的部分,盡管會減少一些趣味性,相信對於入門級的代碼來說這樣也夠用了。
本文實現程序位於代碼庫的 dagoba 目錄下。按照本系列程序的同意規則,要想直接執行各個已完成的步驟,讀者可以在根目錄下的 main.py 找到相應的代碼位置,取消注釋並運行即可。
本程序的所有步驟只需要 Python3 ,測試則使用內置的 unittest , 不需要額外的第三方庫。原則上 Python3.6 以上版本應該都可運行,但我只在 Python3.8.3 環境下完整測試過。
本文實現的程序從最簡單的案例開始,通過每個步驟逐步擴展,最終形成一個完整的程序。這些步驟包括:
接下來依次介紹各個步驟。
回想一下,圖資料庫就是一些點( node )和邊( edge )的集合。現在我們要做出的一個重大決策是如何對節點/邊進行建模。對於邊來說,必須指定它的關聯關系,也就是從哪個節點指向哪個節點。大多數情況下邊是有方向的——父子關系不指明方向可是要亂套的!
考慮到擴展性及通用性問題,我們可以把數據保存為字典( dict ),這樣可以方便地添加用戶需要的任何數據。某些數據是為資料庫內部管理而保留的,為了明確區分,可以這樣約定:以下劃線開頭的特殊欄位由資料庫內部維護,類似於私有成員,用戶不應該自己去修改它們。這也是 Python 社區普遍遵循的約定。
此外,節點和邊存在互相引用的關系。目前我們知道邊會引用到兩端的節點,後面還會看到,為了提高效率,節點也會引用到邊。如果僅僅在內存中維護它們的關系,那麼使用指針訪問是很直觀的,但資料庫必須考慮到序列化到磁碟的問題,這時指針就不再好用了。
為此,最好按照資料庫的一般要求,為每個節點維護一個主鍵( _id ),用主鍵來描述它們之間的關聯關系。
我們第一步要把資料庫的模型建立起來。為了測試目的,我們使用一個最簡單的資料庫模型,它只包含兩個節點和一條邊,如下所示:
按照 TDD 的原則,首先編寫測試:
與原文一樣,我們把資料庫管理介面命名為 Dagoba 。目前,能夠想到的最簡單的測試是確認節點和邊是否已經添加到資料庫中:
assert_item 是一個輔助方法,用於檢查字典是否包含預期的欄位。相信大家都能想到該如何實現,這里就不再列出了,讀者可參考 Github 上的完整源碼。
現在,測試是失敗的。用最簡單的辦法實現資料庫:
需要注意的是,不管添加節點還是查詢,程序都使用了拷貝後的數據副本,而不是直接使用原始數據。為什麼要這樣做?因為字典是可變的,用戶可以在任何時候修改其中的內容,如果資料庫不知道數據已經變化,就很容易發生難以追蹤的一致性問題,最糟糕的情況下會使得數據內容徹底混亂。
拷貝數據可以避免上述問題,代價則是需要佔用更多內存和處理時間。對於資料庫來說,通常查詢次數要遠遠多於修改,所以這個代價是可以接受的。
現在測試應該正常通過了。為了讓它更加完善,我們可以再測試一些邊緣情況,看看資料庫能否正確處理異常數據,比如:
例如,如果用戶嘗試添加重復主鍵,我們預期應拋出 ValueError 異常。因此編寫測試如下:
為了滿足以上測試,代碼需要稍作修改。特別是按照 id 查找主鍵是個常用操作,通過遍歷的方法效率太低了,最好是能夠通過主鍵直接訪問。因此在資料庫中再增加一個字典:
完整代碼請參考 Github 倉庫。
在上個步驟,我們在初始化資料庫時為節點明確指定了主鍵。按照資料庫設計的一般原則,主鍵最好是不具有業務含義的代理主鍵( Surrogate key ),用戶不應該關心它具體的值是什麼,因此讓資料庫去管理主鍵通常是更為合理的。當然,在部分場景下——比如導入外部數據——明確指定主鍵仍然是有用的。
為了同時支持這些要求,我們這樣約定:欄位 _id 表示節點的主鍵,如果用戶指定了該欄位,則使用用戶設置的值(當然,用戶有責任保證它們不會重復);否則,由資料庫自動為它分配一個主鍵。
如果主鍵是資料庫生成的,事先無法預知它的值是什麼,而邊( edge )必須指定它所指向的節點,因此必須在主鍵生成後才能添加。由於這個原因,在動態生成主鍵的情況下,資料庫的初始化會略微復雜一些。還是先寫一個測試:
為支持此功能,我們在資料庫中添加一個內部欄位 _next_id 用於生成主鍵,並讓 add_node 方法返回新生成的主鍵:
接下來,再確認一下邊是否可以正常訪問:
運行測試,一切正常。這個步驟很輕松地完成了,不過兩個測試( DbModelTest 和 PrimaryKeyTest )出現了一些重復代碼,比如 get_item 。我們可以把這些公用代碼提取出來。由於 get_item 內部調用了 TestCase.assertXXX 等方法,看起來應該使用繼承,但從 TestCase 派生基類容易引起一些潛在的問題,所以我轉而使用另一個技巧 Mixin :
實現資料庫模型之後,接下來就要考慮如何查詢它了。
在設計查詢時要考慮幾個問題。對於圖的訪問來說,幾乎總是由某個節點(或符合條件的某一類節點)開始,從與它相鄰的邊跳轉到其他節點,依次類推。所以鏈式調用對查詢來說是一種很自然的風格。舉例來說,要知道 Tom 的孫子養了幾只貓,可以使用類似這樣的查詢:
可以想像,以上每個方法都應該返回符合條件的節點集合。這種實現是很直觀的,不過存在一個潛在的問題:很多時候用戶只需要一小部分結果,如果它總是不計代價地給我們一個巨大的集合,會造成極大的浪費。比如以下查詢:
為了避免不必要的浪費,我們需要另外一種機制,也就是通常所稱的「懶式查詢」或「延遲查詢」。它的基本思想是,當我們調用查詢方法時,它只是把查詢條件記錄下來,而並不立即返回結果,直到明確調用某些方法時才真正去查詢資料庫。
如果讀者比較熟悉流行的 Python ORM,比如 SqlAlchemy 或者 Django ORM 的話,會知道它們幾乎都是懶式查詢的,要調用 list(result) 或者 result[0:10] 這樣的方法才能得到具體的查詢結果。
在 Dagoba 中把觸發查詢的方法定義為 run 。也就是說,以下查詢執行到 run 時才真正去查找數據:
和懶式查詢( Lazy Query )相對應的,直接返回結果的方法一般稱作主動查詢( Eager Query )。主動查詢和懶式查詢的內在查找邏輯基本上是相同的,區別只在於觸發機制不同。由於主動查詢實現起來更加簡單,出錯也更容易排查,因此我們先從主動查詢開始實現。
還是從測試開始。前面測試所用的簡單資料庫數據太少,難以滿足查詢要求,所以這一步先來創建一個更復雜的數據模型:
此關系的復雜之處之一在於反向關聯:如果 A 是 B 的哥哥,那麼 B 就是 A 的弟弟/妹妹,為了查詢到他們彼此之間的關系,正向關聯和反向關聯都需要存在,因此在初始化資料庫時需要定義的邊數量會很多。
當然,父子之間也存在反向關聯的問題,為了讓問題稍微簡化一些,我們目前只需要向下(子孫輩)查找,可以稍微減少一些關聯數量。
因此,我們定義數據模型如下。為了減少重復工作,我們通過 _backward 欄位定義反向關聯,而資料庫內部為了查詢方便,需要把它維護成兩條邊:
然後,測試一個最簡單的查詢,比如查找某人的所有孫輩:
這里 outcome/income 分別表示從某個節點出發、或到達它的節點集合。在原作者的代碼中把上述方法稱為 out/in 。當然這樣看起來更加簡潔,可惜的是 in 在 Python 中是個關鍵字,無法作為函數名。我也考慮過加個下劃線比如 out_.in_ 這種形式,但看起來也有點怪異,權衡之後還是使用了稍微啰嗦一點的名稱。
現在我們可以開始定義查詢介面了。在前面已經說過,我們計劃分別實現兩種查詢,包括主動查詢( Eager Query )以及延遲查詢( Lazy Query )。
它們的內在查詢邏輯是相通的,看起來似乎可以使用繼承。不過遵循 YAGNI 原則,目前先不這樣做,而是只定義兩個新類,在滿足測試的基礎上不斷擴展。以後我們會看到,與繼承相比,把共同的邏輯放到資料庫本身其實是更為合理的。
接下來實現訪問節點的方法。由於 EagerQuery 調用查詢方法會立即返回結果,我們把結果記錄在 _result 內部欄位中。雖然 node 方法只返回單個結果,但考慮到其他查詢方法幾乎都是返回集合,為統一起見,讓它也返回集合,這樣可以避免同時支持集合與單結果的分支處理,讓代碼更加簡潔、不容易出錯。此外,如果查詢對象不存在的話,我們只返回空集合,並不視為一個錯誤。
查詢輸入/輸出節點的方法實現類似這樣:
查找節點的核心邏輯在資料庫本身定義:
以上使用了內部定義的一些輔助查詢方法。用類似的邏輯再定義 income ,它們的實現都很簡單,讀者可以直接參考源碼,此處不再贅述。
在此步驟的最後,我們再實現一個優化。當多次調用查詢方法後,結果可能會返回重復的數據,很多時候這是不必要的。就像關系資料庫通常支持 unique/distinct 一樣,我們也希望 Dagoba 能夠過濾重復的數據。
假設我們要查詢某人所有孩子的祖父,顯然不管有多少孩子,他們的祖父應該是同一個人。因此編寫測試如下:
現在來實現 unique 。我們只要按照主鍵把重復數據去掉即可:
在上個步驟,初始化資料庫指定了雙向關聯,但並未測試它們。因為我們還沒有編寫代碼去支持它們,現在增加一個測試,它應該是失敗的:
運行測試,的確失敗了。我們看看要如何支持它。回想一下,當從邊查找節點時,使用的是以下方法:
這里也有一個潛在的問題:調用 self.edges 意味著遍歷所有邊,當資料庫內容較多時,這是巨大的浪費。為了提高性能,我們可以把與節點相關的邊記錄在節點本身,這樣要查找邊只要看節點本身即可。在初始化時定義出入邊的集合:
在添加邊時,我們要同時把它們對應的關系同時更新到節點,此外還要維護反向關聯。這涉及對字典內容的部分復制,先編寫一個輔助方法:
然後,將添加邊的實現修改如下:
這里的代碼同時添加正向關聯和反向關聯。有的朋友可能會注意到代碼略有重復,是的,但是重復僅出現在該函數內部,本著「三則重構」的原則,暫時不去提取代碼。
實現之後,前面的測試就可以正常通過了。
在這個步驟中,我們來實現延遲查詢( Lazy Query )。
延遲查詢的要求是,當調用查詢方法時並不立即執行,而是推遲到調用特定方法,比如 run 時才執行整個查詢,返回結果。
延遲查詢的實現要比主動查詢復雜一些。為了實現延遲查詢,查詢方法的實現不能直接返回結果,而是記錄要執行的動作以及傳入的參數,到調用 run 時再依次執行前面記錄下來的內容。
如果你去看作者的實現,會發現他是用一個數據結構記錄執行操作和參數,此外還有一部分邏輯用來分派對每種結構要執行的動作。這樣當然是可行的,但數據處理和分派部分的實現會比較復雜,也容易出錯。
本文的實現則選擇了另外一種不同的方法:使用 Python 的內部函數機制,把一連串查詢變換成一組函數,每個函數取上個函數的執行結果作為輸入,最後一個函數的輸出就是整個查詢的結果。由於內部函數同時也是閉包,盡管每個查詢的參數形式各不相同,但是它們都可以被閉包「捕獲」而成為內部變數,所以這些內部函數可以採用統一的形式,無需再針對每種查詢設計額外的數據結構,因而執行過程得到了很大程度的簡化。
首先還是來編寫測試。 LazyQueryTest 和 EagerQueryTest 測試用例幾乎是完全相同的(是的,兩種查詢只在於內部實現機制不同,它們的調用介面幾乎是完全一致的)。
因此我們可以把 EagerQueryTest 的測試原樣不變拷貝到 LazyQueryTest 中。當然拷貝粘貼不是個好注意,對於比較冗長而固定的初始化部分,我們可以把它提取出來作為兩個測試共享的公共函數。讀者可參考代碼中的 step04_lazy_query/tests/test_lazy_query.py 部分。
程序把查詢函數的串列執行稱為管道( pipeline ),用一個變數來記錄它:
然後依次實現各個調用介面。每種介面的實現都是類似的:用內部函數執行真正的查詢邏輯,再把這個函數添加到 pipeline 調用鏈中。比如 node 的實現類似下面:
其他介面的實現也與此類似。最後, run 函數負責執行所有查詢,返回最終結果;
完成上述實現後執行測試,確保我們的實現是正確的。
在前面我們說過,延遲查詢與主動查詢相比,最大的優勢是對於許多查詢可以按需要訪問,不需要每個步驟都返回完整結果,從而提高性能,節約查詢時間。比如說,對於下面的查詢:
以上查詢的意思是從孫輩中找到一個符合條件的節點即可。對該查詢而言,主動查詢會在調用 outcome('son') 時就遍歷所有節點,哪怕最後一步只需要第一個結果。而延遲查詢為了提高效率,應在找到符合條件的結果後立即停止。
目前我們尚未實現 take 方法。老規矩,先添加測試:
主動查詢的 take 實現比較簡單,我們只要從結果中返回前 n 條記錄:
延遲查詢的實現要復雜一些。為了避免不必要的查找,返回結果不應該是完整的列表( list ),而應該是個按需返回的可迭代對象,我們用內置函數 next 來依次返回前 n 個結果:
寫完後運行測試,確保它們是正確的。
從外部介面看,主動查詢和延遲查詢幾乎是完全相同的,所以用單純的數據測試很難確認後者的效率一定比前者高,用訪問時間來測試也並不可靠。為了測試效率,我們引入一個節點訪問次數的概念,如果延遲查詢效率更高的話,那麼它應該比主動查詢訪問節點的次數更少。
為此,編寫如下測試:
我們為 Dagoba 類添加一個成員來記錄總的節點訪問次數,以及兩個輔助方法,分別用於獲取和重置訪問次數:
然後瀏覽代碼,查找修改點。增加計數主要在從邊查找節點的時候,因此修改部分如下:
此外還有 income/outcome 方法,修改都很簡單,這里就不再列出。
實現後再次運行測試。測試通過,表明延遲查詢確實在效率上優於主動查詢。
不像關系資料庫的結構那樣固定,圖的形式可以千變萬化,查詢機制也必須足夠靈活。從原理上講,所有查詢無非是從某個節點出發按照特定方向搜索,因此用 node/income/outcome 這三個方法幾乎可以組合出任意所需的查詢。
但對於復雜查詢,寫出的代碼有時會顯得較為瑣碎和冗長,對於特定領域來說,往往存在更為簡潔的名稱,例如:母親的兄弟可簡稱為舅舅。對於這些場景,如果能夠類似 DSL (領域特定語言)那樣允許用戶根據專業要求自行擴展,從而簡化查詢,方便閱讀,無疑會更為友好。
如果讀者去看原作者的實現,會發現他是用一種特殊語法 addAlias 來定義自己想要的查詢,調用方法時再進行查詢以確定要執行的內容,其介面和內部實現都是相當復雜的。
而我希望有更簡單的方法來實現這一點。所幸 Python 是一種高度動態的語言,允許在運行時向類中增加新的成員,因此做到這一點可能比預想的還要簡單。
為了驗證這一點,編寫測試如下:
無需 Dagoba 的實現做任何改動,測試就可以通過了!其實我們要做的就是動態添加一個自定義的成員函數,按照 Python 對象機制的要求,成員函數的第一個成員應該是名為 self 的參數,但這里已經是在 UnitTest 的內部,為了和測試類本身的 self 相區分,新函數的參數增加了一個下劃線。
此外,函數應返回其所屬的對象,這是為了鏈式調用所要求的。我們看到,動態語言的靈活性使得添加新語法變得非常簡單。
到此,一個初具規模的圖資料庫就形成了。
和原文相比,本文還缺少一些內容,比如如何將資料庫序列化到磁碟。不過相信讀者都看到了,我們的資料庫內部結構基本上是簡單的原生數據結構(列表+字典),因此序列化無論用 pickle 或是 JSON 之類方法都應該是相當簡單的。有興趣的讀者可以自行完成它們。
我們的圖資料庫實現為了提高查詢性能,在節點內部存儲了邊的指針(或者說引用)。這樣做的好處是,無論資料庫有多大,從一個節點到相鄰節點的訪問是常數時間,因此數據訪問的效率非常高。
但一個潛在的問題是,如果資料庫規模非常大,已經無法整個放在內存中,或者出於安全性等原因要實現分布式訪問的話,那麼指針就無法使用了,必須要考慮其他機制來解決這個問題。分布式資料庫無論採用何種數據模型都是一個棘手的問題,在本文中我們沒有涉及。有興趣的讀者也可以考慮 500lines 系列中關於分布式和集群演算法的其他一些文章。
本文的實現和系列中其他資料庫類似,採用 Python 作為實現語言,而原作者使用的是 JavaScript ,這應該和作者的背景有關。我相信對於大多數開發者來說, Python 的對象機制比 JavaScript 基於原型的語法應該是更容易閱讀和理解的。
當然,原作者的版本比本文版本在實現上其實是更為完善的,靈活性也更好。如果想要更為優雅的實現,我們可以考慮使用 Python 元編程,那樣會更接近於作者的實現,但也會讓程序的復雜性大為增加。如果讀者有興趣,不妨對照著去讀讀原作者的版本。
7. 最受大家推崇的 Python 書籍有哪些
嚴正提醒! 想提升學習效率,請務必先找准基準線,提高自己對學習材料的品位。
要知道,這個時代,獲取信息的路徑雖短,獲取優質知識的隱性成本卻很高。選錯學習的「姿勢」,白花錢不說,流失了寶貴時間和學習熱情才坑!
選一本 有專業人士背書 、 久經讀者考驗 的技術教程,你便找到了這個領域的基準線。以此來構建自己的知識體系,相信我,絕對能少走很多彎路。
那麼,哪些Python學習書稱得上是「基準」擔當呢?不妨繼續往下看:
文末有驚喜
文末有驚喜
GitHub上有一位叫皮埃爾·德·沃爾夫(Pierre de Wulf)的童鞋,通過以下方法,檢索到了25本網上引用最多的 Python 書籍。
一起看看都有哪些經典 Python 書吧~
希望這些經典的 Python 書籍能夠讓題主的 Python 學習之路更高效,更踏實!
作者: [美] Mark Lutz
——內容簡介——
這本書全面、深入地介紹了 Python 語言,不管你是編程新手還是 Python 初學者,它將幫助你快速實現使用 Python 編寫高質量,且易於與其他語言和工具集成的代碼。本書每一章都是關於Python語言獨立的內容,並且帶有練習和測試,簡單易學,適合入門。
作者:[美]David Beazley, Brian K. Jones
——內容簡介——
這本獨特的「食譜」介紹了 Python 語言應用在各個領域中的使用技巧和方法,其主題涵蓋了數據結構和演算法,字元串和文本,迭代器和生成器,數據編碼與處理,模塊和包,網路和Web編程,並發,實用腳本和系統管理,測試、調試以及異常,C語言擴展等等內容。
每個「配方」均包含可立即在項目中使用的代碼示例,以及Python應用中常見的問題和通用的解決方案。非常適合具有一定編程基礎的Python程序員閱讀。
Python Cookbook(第3版)中文版
作者: [巴西] Luciano Ramalho
——內容簡介——
Python的簡單性可以使你快速提高生產力,但這通常意味著你沒有使用它所提供的一切。
本書致力於幫助Python開發人員挖掘這門語言及相關程序庫的優秀特性,避免重復勞動,同時寫出簡潔、流暢、易讀、易維護,並且具有地道Python風格的代碼。本書尤其深入探討了Python語言的 高級用法 ,涵蓋數據結構、Python風格的對象、並行與並發,以及元編程等不同的方面。通過本書,Python程序員將全面學習如何精通Python 3。
作者: [美] Eric Matthes
——內容簡介——
本書是一本針對所有層次的 Python 讀者而作的 Python 入門書。
全書分兩部分:第一部分介紹用Python 編程所必須了解的 基本概念 ,包括matplotlib、NumPy 和Pygal 等強大的Python 庫和工具介紹,以及列表、字典、if 語句、類、文件與異常、代碼測試等內容;第二部分將理論付諸 實踐 ,講解如何開發三個項目,包括簡單的 Python 2D 游戲 開發,如何利用數據生成互動式的信息圖,以及創建和定製簡單的 Web 應用,並幫讀者解決常見編程問題和困惑。
作者:Paul Barry
——內容簡介——
你是否想學習 Python 而不用費心地看手冊?
本書通過一種獨特的超越語法手冊的方式來幫助你學習Python。你將能夠快速掌握 Python 的基礎知識,然後擴展到持久化、異常處理、Web開發、SQLite、數據處理和Google應用引擎中去。你也將學會如何為 Android 編寫移動應用,這要感謝Python帶給你的強大能力。本書融合了完備的學習經驗,它將幫助你快速成為一名真正的 Python 程序員。
作者: [美]Albert Sweigart
——內容簡介——
如果你花了數小時重命名文件或更新了數百個電子表格單元格,你就會知道像這樣的任務多麼繁瑣。 但是,如果可以讓你的計算機為你做這些事情呢?可能只用幾分鍾吧。
本書是一本面向實踐的Python編程實用指南。你將學習 Python 的基礎知識,並 探索 用 Python 豐富的模塊庫來執行任務,例如從網站上抓取數據,閱讀PDF和Word文檔以及自動執行單擊和鍵入任務等。
通過閱讀本書,讀者將學會利用強大的 Python 語言和工具,並且會體會到 Python 編程的快樂。
作者: [美] Zed A.Shaw
——內容簡介——
本書是一本 Python 入門書籍。作者 Zed Shaw 完善了這個堪稱世上最好的 Python 學習系統。只要跟著學習,你就會和迄今為止數十萬 Zed 教過的初學者一樣獲得成功。
這本書以習題的方式引導讀者一步一步學習編程,從簡單的列印一直講到完整項目的實現,讓初學者從基礎的編程技術入手,最終體驗到軟體開發的基本過程。你將學會怎樣閱讀、編寫、思考代碼,以及如何用專業程序員的技巧來找出並修正錯誤。
作者:Wes McKinney
——內容簡介——
本書由 Python pandas 項目創始人 Wes McKinney 親筆撰寫,詳細介紹利用 Python 進行操作、處理、清洗和規整數據等方面的具體細節和基本要點。雖然「數據分析」是本書的標題,但重點是Python編程,庫和工具,而不是數據分析方法。這是數據分析所需的 Python 編程。
作者: [美]布雷特·斯拉特金(Brett Slatkin)
——內容簡介——
用 Python 編寫程序是相當容易的,所以這門語言非常流行。但若想掌握 Python 所特有的優勢、魅力和表達能力,則相當困難,而且語言中還有很多隱藏的陷阱,容易令開發者犯錯。本書可以幫你掌握真正的 Pythonic 編程方式,令你能夠完全發揮出 Python 語言的強大功能,並寫出健壯而高效的代碼。
作者: [美] 艾倫 B. 唐尼
——內容簡介——
本書以培養讀者 以計算機科學家一樣的思維方式 來理解Python語言編程。貫穿全書的主體是如何思考、設計,以及開發的方法。
全書詳細介紹了 Python 編程語言的方方面面,從基本的編程概念到函數,遞歸,數據結構和面向對象編程等等。每一章都配有術語表和練習題,方便讀者鞏固所學的知識和技巧。此外,作者針對每章所專注的語言特性,或者相關的開發問題,總結了調試的方方面面。
作者:[德]達恩·巴德爾(Dan Bader)
——內容簡介——
這本書將通過簡單的示例和分步說明來介紹 Python 的最佳實踐以及 Python 代碼的強大魅力。藉助本書,你將專注於真正重要的實踐技能,在 Python 的標准庫中發現「隱藏的金子」,距離精通 Python 更近一步!
作者:[美]Sebastian Raschka, Vahid Mirjalili
——內容簡介——
本書是關於使用 Python 進行機器學習和深度學習的綜合指南。 它既是分步教程,又是構建機器學習系統時不斷翻閱的參考書。
本書包含清晰的注釋,可視化效果和工作示例,深入介紹了基本上所有的機器學習技術。 雖然有些書只教您遵循說明,但在本書中,作者講授了機器學習的原理,這使你可以自己構建模型和應用程序。
作者: Mark Pilgrim
——內容簡介——
有很多 python 開發人員需要學習將代碼移植到python 3,而本書是為他們提供最新版本 python 介紹的最佳書籍。它獨特的風格是先提供大量代碼然後將其分解,非常適合希望快速了解新版本語言的現有開發人員。
作者: [美] David M.Beazley
——內容簡介——
本書是 Python 編程語言的權威參考指南,內容涉及核心 Python 語言和 Python 庫的最重要部分,內容簡潔扼要、可讀性強。書中還包括了一些沒有在 Python 官方文檔或其他資料中出現過的一些高級主題的詳細信息。
這一版在內容上進行了全面更新,介紹了 Python 2.6 和 Python 3 新引入的編程語言特性和庫模塊,同時還分析了Python程序員面臨的如下難題:是應繼續使用現有的 Python 代碼,還是應制定計劃將其移植到Python 3?
作者: 盧茨 (Mark Lutz)
——內容簡介——
當掌握 Python 的基礎知識後,你要如何使用 Python?本書為這門語言的主要應用領域提供了深度教程,譬如系統管理、GUI 和 Web,並 探索 了其在資料庫、網路、前端腳本、文本處理等方面的應用。你將學到清晰、簡潔明了的語法和編程技巧,並伴隨大量的示例來展示正確的用法和慣例。
作者: [德]安德里亞斯·穆勒,[美]莎拉·吉多
——內容簡介——
本書是機器學習入門書,以 Python 語言介紹。
書中重點討論機器學習演算法的實踐而不是背後的數學,全面涵蓋在實踐中實現機器學習演算法的所有重要內容,幫助讀者使用 Python 和 scikit-learn 庫一步一步構建一個有效的機器學習應用。
本書將向所有對機器學習技術感興趣的初學者展示,自己動手構建機器學習解決方案並非難事!
作者: [美] Alex Martelli,Anna Ravenscroft,Steve Holden
——內容簡介——
本書適合具有一定 Python 編程經驗或者有其他語言編程基礎的程序員閱讀,它涵蓋了廣泛的應用領域,包括 Web和網路編程,XML處理,資料庫交互以及高速數值計算。該實用手冊的第三版提供了對該語言的快速參考(包括Python 3.5、2.7和3.6的亮點)。
作者: [美] Al Sweigart
——內容簡介——
本書通過編寫一個個小巧、有趣的 游戲 來教授Python編程,並且採用直接展示 游戲 的源代碼並通過實例來解釋編程的原理的方式。首先構建 猜數字 和 Tic Tac Toe 這樣的經典 游戲 ,然後逐步開發更高級的 游戲 ,在此過程中,你將學習關鍵的編程和數學概念,這將幫助你在輕松有趣的過程中,掌握 Python 游戲 編程的基本技能。
全書共21章,12個 游戲 程序和示例貫穿其中,介紹了Python基礎知識、數據類型、函數、流程式控制制、程序調試、流程圖設計、字元串操作、列表和字典、笛卡爾坐標系、密碼學基礎、 游戲 AI模擬、動畫圖形、碰撞檢測、聲音和圖像等方方面面的程序設計知識。
作者:Mark Lutz
——內容簡介——
對於新的Python 3.4 和 2.7 而言,這款便捷的袖珍指南是完美的實戰快速參考書。你將會從中學習有關Python類型和語句,特殊方法名,內建函數與異常,常用的標准庫模塊及其他卓越的Python工具。
作者:[美] Kenneth Reitz,Tanya Schlusser
——內容簡介——
本書是 Python 用戶的一本網路式學習指南,本書由 Python 社區的大神 Kenneth Reitz 發起並組織編寫,由社區數百名開發者集體奉獻。其特色在於,近乎完整地總結了在 Python 編程中會用到的各種實踐技巧和經驗,涵蓋眾多主流的應用場景,並告訴你如何提高效率、避免踩坑、編寫高質量的代碼。
本書著重於可重用的代碼,重於設計理念,將讀者引向已存在的優秀資源,適合有一定Python基礎的人員學習,幫助你迅速從小工修煉成專家,編寫出高質量的代碼!
========
以上python書籍我已整理電子版本
獲取方式:
轉發+私信「1」獲取,
私信方式:第一步:點擊頭像
旁邊有個私信按鈕,點擊回復「1」即可!
8. 學習python編程小白要看哪些書
1、《父與子編程:與小卡特一起學python》
這本書是零基礎看的書籍,對於很多剛接觸編程的小白來說,對於很多名詞是很難理解的,本書把知識點拆的很細,更難得的是那些難理解的技術名詞,這本書都是用場景對話、動畫的形式展現,讓初學者更容易學會;看了它,你就會發現python是多麼容易。
2、《python cookbook》
本書介紹了應用在各個領域的技巧和方法,涵蓋了很多高級主題:元編程、網路、web編程、實用腳本、系統管理、C語言拓展等。本書還覆蓋了許多python日常開發中的問題以及解決方案,在實際開發中的編程技巧,告訴你python如何工作並且為什麼工作。
3、《python高手之路》
眾所周知,項目經驗以及底層知識拓展對於一個程序員的進階來說是非常重要的!《python高手之路》它是從一個項目的第一步開始,從整個項目的架構設計、對模塊和庫的處理來構建一個完整的項目。
9. python是面向什麼的高級語言
面相對象、解釋型。
Python支持多種編程范型,包括函數式、指令式、反射式、結構化和面向對象編程。它擁有動態類型系統和垃圾回收功能,能夠自動管理內存使用,並且其本身擁有一個巨大而廣泛的標准庫。它的語言結構以及面向對象的方法旨在幫助程序員為小型的和大型的項目編寫清晰的、合乎邏輯的代碼。
吉多·范羅蘇姆於1980年代後期開始研發Python,它是作為ABC語言的後繼者,也可以視之為使用叫做的M-表達式的一種傳統中綴表示法的LISP方言。
吉多·范羅蘇姆於1991年首次發布 Python 0.9.0。Python2.0於2000 年發布並引入了新功能。Python3.0於2008年發布,是該語言的主要修訂版,並非完全向後兼容。 Python2於2020年隨2.7.18版停止使用。
Python的設計哲學強調代碼的可讀性和簡潔的語法,尤其是使用空格縮進劃分代碼塊。相比於C或Java,Python讓開發者能夠用更少的代碼表達想法。
Python解釋器本身幾乎可以在所有的操作系統中運行。Python的官方解釋器CPython是用C語言編寫的,它是一個由社群驅動的自由軟體,目前由Python軟體基金會管理。
特徵
Python是多范型編程語言。它完全支持結構化編程和面向對象編程,還有很多特徵支持函數式編程和元編程比如元對象協議(元類和魔術方法)。
通過擴展還可以支持很多范型,包括面向切面編程、契約式設計和邏輯編程。
Python使用動態類型,在內存管理上採用引用計數和環檢測相結合的垃圾收集器。它的特徵還有動態名字解析(後期綁定),即在程序執行期間綁定方法和變數的名字。
Python對遵循LISP傳統的函數式編程提供了有限的支持,它提供了map、filter和rece函數;列表推導式、字典、集合和生成器表達式。
10. python3.5與python3.10區別
Python3.5和Python3.10的主要區別在於新增的功能和更新。Python3.5新增了一些新的特性,如元編程、新的語法元素、正則表達式的壓縮模式、高級索引,以及新的類型和模塊,而Python3.10新增了新的語言功能,如模塊聲明及標志、 Python類型檢查、更好的編程模型、改進的調試功能等。