標籤:
?? 語句和文法?? 變數賦值?? 標識符和關鍵字?? 基本風格指南?? 記憶體管理?? 第一個 Python 程式3.1語句和文法Python 語句中有一些基本規則和特殊字元:
?? 井號(#)表示之後的字元為 Python 注釋
?? 換行 (\n) 是標準的行分隔字元(通常一個語句一行)
?? 反斜線 ( \ ) 繼續上一行
?? 分號 ( ; )將兩個語句串連在一行中
?? 冒號 ( : ) 將代碼塊的頭和體分開
?? 語句(代碼塊)用縮排塊的方式體現 (縮排四個空格寬度,避免使用定位字元)
?? 不同的縮排深度分隔不同的代碼塊
?? Python 檔案以模組的形式組織 (當一個模組變得過大,並且驅動了太多功能的話,就應該考慮拆一些代碼出來另外建一個模組.)注釋( # ) 和很多 Unix 指令碼類似,Python 備註陳述式從 # 字元開始,注釋可以在一行 的任何地方開始,解譯器會忽略掉該行 # 之後的所有內容。要正確的使用注釋。繼續( \ ) Python 語句,一般使用換行分隔,也就是說一行一個語句。一行過長的語句可以使用反斜 杠( \ ) 分解成幾行,如下例: # check conditions if (weather_is_hot == 1) and \ (shark_warnings == 0): send_goto_beach_mesg_to_pager() 有兩種例外情況一個語句不使用反斜線也可以跨行。 在使用閉合操作符時,單一語句可以 跨多行,例如:在含有小括弧、中括弧、花括弧時可以多行書寫。 另外就是三引號包括下的字串也可以跨行書寫。 如果要在使用反斜線換行和使用括弧元素換行作一個選擇,我們推薦使用括弧,這樣可讀 性會更好。多個語句構成程式碼群組(:): 縮排相同的一組語句構成一個代碼塊,我們稱之程式碼群組。 像if、while、def 和class 這樣 的複合陳述式,首行以關鍵字開始,以冒號( : )結束,該行之後的一行或多行代碼構成程式碼群組。我們將首行及後面的程式碼群組稱為一個子句(clause)。程式碼群組由不同的縮排分隔 代碼的層次關係是通過同樣 深度的空格或定位字元縮排體現的。 核心風格:縮排四個空格寬度,避免使用定位字元 對一個初次使用空白字元作為代碼塊分界的人來說,遇到的第一個問題是:縮排多大寬度 才合適?兩個太少,六到八個又太多,因此我們推薦使用四個空格寬度。 需要說明一點,不同的文字編輯器中定位字元代表的空白寬度不一,如果你的代碼要跨平台應用,或者會被不同的編輯器讀寫,建議你不要使用定位字元。 使用空格或定位字元這兩種風格都得到了Python 創始人Guido van Rossum 的支援,並被收錄到Python 代碼風格指南文檔。同一行書寫多個語句(;) 分號( ; )允許你將多個語句寫在同一行上,語句之間用分號隔開,而這些語句也不能在這 行開始一個新的代碼塊。 必須指出一點, 同一行上書寫多個語句會大大降低代碼的可讀性,Python 雖然允許但不 提倡你這麼做。模組 每一個Python 指令檔都可以被當成是一個模組。模組以磁碟檔案的形式存在。當一個模 塊變得過大,並且驅動了太多功能的話,就應該考慮拆一些代碼出來另外建一個模組。 模組裡的代碼可以是一段直接執行的指令碼,也可以是一堆類似庫函數的代碼,從而可以被別的模組匯入(import)調用。 模組可以包含直接啟動並執行代碼塊、類定義、函數定義或這幾者的組合。
3.2 變數賦值
賦值運算子 Python 語言中, 等號(=)是主要的賦值運算子。 注意,賦值並不是直接將一個值賦給一個變數, 儘管你可能根據其它語言編程經驗認為應該如此。在Python 語言中,對象是通過引用傳遞的。在賦值時,不管這個對象是新建立的,還是一個已經存在的,都是將該對象的引用(並不是值)賦值給變數。同樣的, 如果你比較熟悉C, 你會知道指派陳述式其實是被當成一個運算式(可以傳回值)。不過這條並不適合於 Python, Python 的指派陳述式不會傳回值。鏈式賦值
>>> y = x = x + 1>>> x, y(2, 2)
增量賦值
>>> x = 1>>> x += 1>>> x2
多元賦值 (採用這種方式賦值時, 等號兩邊的對象都是元組)
>>> x, y, z = 1, 2, ‘a string‘>>> x1>>> y2>>> z‘a string‘Python 的多元賦值方式可以實現無需中間變數交換兩個變數的值。
3.3 標識符
合法的Python 標識符 Python 標識符字串規則和其他大部分用C 編寫的進階語言相似: ?? 第一個字元必須是字母或底線(_)
?? 剩下的字元可以是字母和數字或底線
?? 大小寫敏感關鍵字
內建
除了關鍵字之外,Python 還有可以在任何一級代碼使用的“內建”的名字集合,這些名字 可以由解譯器設定或使用。 雖然built-in 不是關鍵字,但是應該把它當作“系統保留字”,不做他用。 然而,有些情況要求覆蓋(也就是:重定義,替換)它們。 Python 不支援重載標識符,所以任何時刻都只有一個名字綁定。 我們還可以告訴進階讀者built-in 是__builtins__模組的成員,在你的程式開始或在互動 解譯器中給出>>>提示之前,由解譯器自動匯入的。 把它們看成適用在任何一級Python 代碼的全域變數。專用底線標識符 Python 用底線作為變數首碼和尾碼指定特殊變數。
總結:?? _xxx 不用‘from module import *‘匯入?? __xxx__系統定義名字
?? __xxx 類中的私人變數名
核心風格:避免用底線作為變數名的開始 因為底線對解譯器有特殊的意義,而且是內建標識符所使用的符號,我們建議程式員避免用底線作為變數名的開始。 一般來講,變數名_xxx 被看作是“私人的”,在模組或類外不可以使用。 當變數是私人的時候,用_xxx 來表示變數是很好的習慣。因為變數名__xxx__對Python 來說有特殊含義,對於普通的變數應當避免這種命名風格。3.4 基本風格指南 注釋,文檔,縮排,標識符 模組結構和布局 # (1) 起始行(Unix) # (2) 模組文檔 # (3) 模組匯入 # (4) 變數定義 # (5) 類定義 # (6) 函數定義 # (7) 主程式 (1) 起始行 通常只有在類Unix 環境下才使用起始行,有起始行就能夠僅輸入指令碼名字來執行指令碼,無 需直接調用解譯器。 (2)模組文檔 簡要介紹模組的功能及重要全域變數的含義,模組外可通過 module.__doc__ 訪問這些內 容。 (3)模組匯入 匯入當前模組的代碼需要的所有模組;每個模組僅匯入一次(當前模組被載入時);函數內部的模組匯入代碼不會被執行, 除非該函數正在執行。 (4)變數定義 這裡定義的變數為全域變數,本模組中的所有函數都可直接使用。 從好的編程風格角度說, 除非必須,否則就要盡量使用局部變數代替全域變數,如果堅持這樣做,你的代碼就不但容易維護,而且還可以提高效能並節省記憶體。 (5)類定義語句 所有的類都需要在這裡定義。當模組被匯入時class 語句會被執行, 類也就會被定義。類的文檔變數是class.__doc__。 (6)函數定義語句 此處定義的函數可以通過module.function()在外部被訪問到,當模組被匯入時 def 語句會被執行,函數也就都會定義好,函數的文檔變數是function.__doc__。 (7) 主程式 無論這個模組是被別的模組匯入還是作為指令碼直接執行,都會執行這部分代碼。通常這裡 不會有太多功能性代碼,而是根據執行的模式調用不同的函數。
Figure 3–1 Typical Python file structure 推薦代碼風格:主程式調用main()函數
主程式碼通常都和你前面看到的代碼相似,檢查 __name__ 變數的值然後再執行相應的 調用(參閱下一頁的核心筆記)。
主程式中的代碼通常包括變數賦值, 類定義和函數定義,隨後檢查__name__來決定是否調用另一個函數(通常調用main()函數)來完成該模組的功能。 主程式通常都是做這些事。(我們上面的例子中使用test()而不是main()是為了避免你在讀到核心筆記前感到迷惑。)
不管用什麼名字,我們想強調一點那就是:這兒是放置測試代碼的好地方。大部分的Python 模組都是用於匯入調用的,直接運行模組應該調用該模組的迴歸測試代碼。 請記住,絕大部分的模組建立 的目的是為了被別人調用而不是作為獨立執行的指令碼。 只有一個模組,也就是包含主程 序的模組會被直接執行,或由使用者通過命令列執行,或作為批處理執行, 或由Unix cron 任務定時執行,或通過Web 服務器調用,或通過GUI 執行。 核心筆記:__name__ 指示模組應如何被載入 如果模組是被匯入, __name__ 的值為模組名字 如果模組是被直接執行, __name__ 的值為 ‘__main__‘3.5 記憶體管理
?? 變數無須事先聲明
?? 變數無須指定類型
?? 程式員不用關心記憶體管理
?? 變數名會被“回收”
?? del 語句能夠直接釋放資源
變數定義
在Python 中,無需顯式變數聲明語句,變數在第一次被賦值時自動聲明。
和其他大多數語言一樣,變數只有被建立和賦值後才能被使用。
動態類型
Python 中不但變數名無需事先聲明,而且也無需型別宣告。
Python 語言中, 對象的類型和記憶體佔用都是運行時確定的。
儘管代碼被編譯成位元組碼,Python 仍然是一種解釋型語言。
在建立--也就是賦值時,解譯器會根據文法和右側的運算元來決定新對象的類型。
在對象建立後,一個該對象的引用會被賦值給左側的變數。記憶體配置 作為一個負責任的程式員,我們知道在為變數分配記憶體時,是在借用系統資源,在用完之 後, 應該釋放借用的系統資源。 Python 解譯器承擔了記憶體管理的複雜任務, 這大大簡化了應用程式的編寫。 你只需要關心你要解決的問題,至於底層的事情放心交給Python 解譯器去做就行了。引用計數 每個對象各有多少個引用, 簡稱引用計數。
一個引用計數內部跟蹤變數,稱為一個引用計數器。
當這個對象不再需要時, 也就是說, 這個對象的引用計數變為0 時, 它被記憶體回收。增加引用計數 當對象被建立並(將其引用)賦值給變數時,該對象的引用計數就被設定為1。 當同一個對象(的引用)又被賦值給其它變數時, 或作為參數傳遞給函數, 方法或類執行個體 時, 或者被賦值為一個視窗對象的成員時,該對象的一個新的引用,或者稱作別名,就被建立(則該對象的引用計數自動加1)。對象的引用計數在 ?? 對象被建立 x = 3.14 ?? 或另外的別名被建立 y = x ?? 或被作為參數傳遞給函數(新的本地引用) foobar(x) ?? 或成為容器物件的一個元素 myList = [123, x, ‘xyz‘]減少引用計數 當對象的引用被銷毀時,引用計數會減小。 最明顯的例子就是當引用離開其作用範圍時,這種情況最經常出現在函數運行結束時,所有局部變數都被自動銷毀,對象的引用計數也就隨之減少。 當變數被賦值給另外一個對象時,原對象的引用計數也會自動減1: foo = ‘xyz‘ #‘xyz‘ 1 bar = foo #‘xyz‘ 2 foo = 123 #‘xyz‘ 1 其它造成對象的引用計數減少的方式包括使用 del 語句刪除一個變數(參閱下一節), 或 者當一個對象被移出一個視窗對象時(或該容器物件本身的引用計數變成了0 時)。 一個對象的引用計數在以下情況會減少: ?? 一個本地引用離開了其作用範圍。比如 foobar()(參見上一下例子)函數結束時。 ?? 對象的別名被顯式的銷毀。 del y # or del x ?? 從現在的名字空間中刪除 y ?? x 的引用計數減一 ?? 對象的一個別名被賦值給其它的對象 x = 123 ?? 對象被從一個視窗對象中移除 myList.remove(x) ?? 視窗對象本身被銷毀 del myList # or goes out-of-scope垃圾收集 不再被使用的記憶體會被一種稱為垃圾收集的機制釋放。 雖然解譯器跟蹤對象 的引用計數, 但垃圾收集器負責釋放記憶體。 垃圾收集器是一塊獨立代碼, 它用來尋找引用計數為0 的對象。 它也負責檢查那些雖然引用計數大於0 但也應該被銷毀的對象。 特定情形會導致循環參考。 一個循環參考發生在當你有至少兩個對象互相引用時, 也就是說所有的引用都消失時, 這 些引用仍然存在, 這說明只靠引用計數是不夠的。 Python 的垃圾收集器實際上是一個引用計數器和一個迴圈垃圾收集器。 當一個對象的引用計數變為0,解譯器會暫停,釋放掉這個對象和僅有這個對象可訪問(可到達)的其它對象。 作為引用計數的補充, 垃圾收集器也會留心被分配的總量很大(及未通過引用計數銷毀的那些)的對象。 在這種情況下, 解譯器會暫停下來, 試圖清理所有未引用的迴圈。核心技巧:使用局部變數替換模組變數
類似 os.linesep 這樣的名字需要解譯器做兩次查詢:
(1)尋找os 以確認它是一個模組,
(2)在這個模組中尋找 linesep 變數。因為模組也是全域變數, 我們多消耗了系統資源。
如果你在一個函數中類似這樣頻繁使用一個屬性,我們建議你為該屬性取一個本地變數別名。
變數尋找速度將會快很多--在尋找全域變數之前, 總是先尋找本地變數。
這也是一個讓你的程式跑的更快的技巧: 將經常用到的模組屬性替換為一個本地引用。代碼跑得更快,而也不用老是敲那麼長的變數名了。3.6 相關模組和開發工具 Python 代碼風格指南(PEP8), Python 快速參考和Python 常見問答都是開發人員很重要的“工具”。 另外, 還有一些模組會協助你成為一個優秀的Python 程式員。
?? Debugger: pdb
?? Logger: logging
?? Profilers: profile, hotshot, cProfile
03 Python 基礎 - 《Python 核心編程》