Python 筆記(一)

Written by Python高效編程

應用

  • 大文本查找單詞並修改
  • 重命名圖片或者文件
  • 小型數據庫
  • 特定目的的 GUI
  • 簡單的小遊戲

流程:寫程序/編譯/測試/重編譯 過程漫長

shell 適合移動、修改文件,但不適合用戶界面或者遊戲

c/c++/Java 耗時過長,即使是初稿

Python 使用起來更簡單,跨平臺,幫助你更快速地完成工作。

Python 雖然簡單易用,但也是通用編程語言。相較於其他腳本語言,Python 提供了多得多的適合於大型項目的結構與支持。作為高級語言,Python 相較於 C 語言提供更多的錯誤檢查,並且有高度封裝的數據類型,比如靈活的矩陣和字典。Python 相較於 Awk 甚至是 Perl,事何更加複雜的領域,儘管許多方面使用起來差不多方便。

Python 允許你把程序分割成可以重複使用的模塊,你可以將模塊用在其他 Python 項目中。你還可以使用 Python 內置的標準模塊,或者作為開始學習 Python 的範例。一些模塊提供:文件讀取寫入,系統調用,sockets編程,甚至是用戶界面接口工具 TK。

Python 是解釋型語言,編譯和linking是不必要的,在程序開發的時候,可以節省可觀的時間。你可以通過交互式終端執行 Python 程序,因此很容易就可以實驗語言的特性,寫一些一次性的程序,或者在自下而上開發時測試函數。同時,Python 是容易使用的桌面計算器。

可擴展性強

非正式介紹

  • 數值類型:浮點型,整型,複數(j or J) , 字符串
  • 各種操作符 :// , / , * ,** ,%,+/-
  • 先賦值,再使用
  • 整型 + 浮點數 = 浮點數
  • 交互模式 _ = ans

字符串

  • ‘ . . . ’ or “ . . . ” 單引號或者雙引號
  • \ 可以忽略‘ “ it’s ok!” 或者 ‘ It'ok ’ 或者 ‘ yes “好的呢” ’
  • 交互模式,輸出字符以引號括起,特殊字符仍保留(\n)
  • print 沒有引號,沒有特殊字符
  • 不想 \被解釋為特殊字符,使用原生字符串(raw strings)在第一個引號前
  • 字符串可以跨越多行。一種方式是使用三個引號‘''...'’'或者·“""...""”
  • 也可以使用 \ 註釋
  • +連*倍
  • ‘Py’ ‘thon' 連
  • 當你想斷開長字符,這個方法特別又要
  • 只有個部分均為字符串文字(literals),而不是變量或者表達式
  • 連接變量與文字 請使用 +
  • 支持 index 0 起始
  • 沒有單獨的字符類型(char),一個字符就是長度為1的字符串
  • 支持負數下標,從右開始 -1
  • 切片操作
  • 索引獲取單個字符,切片獲取子字符串
  • s = s[:i] + s[i:]
  • 省略的第一個下標默認為 0
  • 省略的第二個下標默認為字符串長度
  • 標上序號記憶
  • 左開右閉
  • 索引不能超過大小
  • 而切片卻可以
  • strings 常量不可修改(immutable:numbers,strings,tuples:(1,2))
  • word[0] = ‘J’
  • len(s) 返回字符串大小

列表

  • 複合數據類型
  • 寫作方括號與逗號的組合
  • 支持索引與切片
  • [:] 淺拷貝
  • 支持+*
  • 可修改 cube(立方)
  • append 後插
  • 賦值 可以改變列表大小,甚至清空
  • letters[2:5] = [‘d’,‘e’] or []
  • 支持 len
  • 嵌入子列表

更加複雜的任務

# 斐波那契數列
a, b = 0,1
while b < 10:
print(b)
a, b = b, a + b
  • 多賦值 a,b = b,a simultaneously
  • 表達式順序
  • 左右 = 首先執行
  • while 一直執行,只要判斷條件成立
  • 類似 C,任何非零值都是成立的,0 是 False。
  • 字符串、列表、任何不為空的序列。
  • 各種比較符 與 C 類似
  • 循環的主體是要縮進的,縮進是 Python 組成語句的方式。
  • Tab 四個字符縮進
  • print 打印多個參數,浮點數,字符串
  • 字符串無引號,以逗號隔開的元素,打印時會用空格隔開
  • end 參數用於分隔每個 print 打印的內容 ‘,’ 避免換行
  • 優先級

if 語句

  • 類似其他語言的條件語句
  • if x < 0 :
  • elif x == 0:
  • else:
  • elif 可以有 0 個,或是更多,else是可選的
  • elif 是 else if 的縮寫,避免了不必要的1縮進
  • 可替代 switch case

for 語句

  • 有些不同 相對於 C 和 Pascal
  • C 中既定義了迭代條件和停止條件
  • for 迭代 任何序列的元素(列表或者字符串),按照出現順序
  • words
  • 如果迭代時你想修改序列值,推薦先做個拷貝
  • 迭代並不會拷貝數值
  • 切片隱式拷貝[:] 特別方便
  • 不拷貝,會無限迭代,創建無窮列表
words = ['cat', 'window', 'defenestrate']
for w in words[:]:
if len(w) > 6:
words.insert(0, w)

range() 函數

  • 迭代數字序列
  • range(10) 序列大小為 10
  • 合理的索引值為 10
  • 不包含結束點,默認初始值為 0
  • 可以設置間隔或者步長(甚至可以是負數)
  • range(-10, -100, -30)
  • 為了可以迭代序列的索引,可以同時使用 range 和 len
  • enumerate()
  • strange: print(range(10))
  • range 似乎是個列表,實際上不是

它是一個當你迭代它時,返回迭代序列連續元素的對象。但是它並不生成列表,因此節省了空間。這樣的對象是可迭代的,這種結構的特點是:你可以連續從中獲取元素,直到沒有為止。for 語句是迭代器,函數list()也是迭代器,它可以從可迭代對象中創建列表

break continue else

  • break 跳出最內層循環,只跳出一層循環
  • else 子句
  • 當 for 循環結束,執行else 或者 while 判斷條件為 False
  • 與 for 一起使用 類似於與 try 一起使用
  • try : else 用於沒有異常發生,for : else 用於沒有 break 語句
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, '*', n//x)
break
else:
# loop fell through without finding a factor
print(n, 'is a prime number')

  • continue 也是從 C 中借用的語句,繼續開始下一次的迭代

pass

  • pass 語句什麼都不做,當語法上需要但是不需要執行的時候
  • While True : pass
  • class MyEmptyClass : pass
  • 函數佔位,條件主體,允許你考慮更抽象的水平

定義函數

def fib(n):
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
  • 創建斐波那契函數到任意邊界
  • def 關鍵字用於定義函數 + 函數名 + (參數)
  • 語句形成了函數的主體。必須被縮進
  • 函數主體的第一條語句可以是字符串
  • 字符串是函數的說明文檔(docstring)
  • 養成說明的習慣
  • 函數的執行引進了新的符號表,局部變量

更準確地說,在函數中分配的變量,儲存在局部符號表格中(locak symbol table)

變量引用查找局部變量符號表格,然後是封閉函數的局部符號表格,接著是全局符號表格,最後是內置函數名。因此,全局變量不能直接在函數中直接賦值給全局變量(除非使用 global 語句),儘管我們可以引用全局變量。

函數的實參,在被調用函數的局部符號表格中。因此函數傳遞參數的引用,而不是參數的值。數值不會被改變(常量),列表會被改變。

當函數調用另一個函數,這次調用會創建新的局部符號表格。

函數定義在當前符號表格中引進了新的函數名。函數名的值的類型被解釋器當成用戶定義函數。這個值可以分配其他名字,我們可以通過其他名字調用這個函數,這就是重命名機制的原理:

fib
f = fib
f(100)

函數沒有 return 語句也會返回值,return None,None 被解釋器抑制了。如果它是唯一寫入的值,對於交互模式解釋器,使用print。

  • return 語句從函數中返回數值,return 後面沒有表達式參數的話,會返回 None。
  • ulist.append 被稱為 list 對象 ulist 的方法。這個方法是屬於一個對象的函數,被命名為 obj.methodname,其中 obj 是某些對象(也可能是表達式)。而 methodname 是由對象類型確定的方法名。不同的對象確定不同的方法。不同對象的方法可能會有相同的方法名,卻不會引起歧義。你可以是由 class 定義你自己的對象類型。append 正是 list 對象的方法,它在 list 的末尾增加一個新元素,這等同於 result = result + [a],但是更加高效。

更多關於定義函數的知識

默認參數值

  • 設定默認值
  • def ask_ok(prompt, retries=4, reminder=‘again’)
  • 只給一個強制參數
  • 給出一個可選參數
  • 給出全部參數
  • in 用於判斷一個序列是否有某個值
  • 默認值在函數定義前給出
i = 5
def f(arg=i):
print(arg)
i = 6
f()
# 打印 5

警告:

初始值只被計算一次。這使得當初始值是可變對象(列表,字典,或者大多數類),會有一些不同。舉個例子,下面的函數會累計傳遞給序列的參數。

def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
輸出:
[1]
[1, 2]
[1, 2, 3]
def f(a, L=None):
if L is None:
L = []
L.append(a)

return L

關鍵字(Keyword Arguments)

通過使用關鍵字參數(kwarg=value),我們可以調用函數。

  • 關鍵字參數 必須在位置參數之後
  • 非關鍵字參數要在關鍵字參數之前
  • 正確:12, state = ‘jump’
  • 錯誤:voltage = 12, ‘jump’
  • 每個參數不能重複接受值

*arguments **keywords

  • **name 接受字典 keyword = value return dict
  • *name 接受元組包含所有的位置參數,超出正式參數列表
  • *name 必須在 **name 的前面
def f(*age,**name):
print(age)
print(name)
f(1,4,'sd',key = 'sd',time = '12')

任意參數列表

  • 最後,最不常用的的選項可以用任意數目參數,這些參數將會被包裹在元組中,零個或者多個參數都可以。
  • 可變參數(variadic),必須在正式參數的最後,因為它們接受剩餘所有傳遞給函數的輸入參數
  • 任何在 *arg 後面的參數,必須是“keyword-only”參數,也就是說,必須以關鍵字的形式而不是位置參數形式使用關鍵字
def concat(*args, sep="/"):
print(sep.join(args))

concat("earth", "mars", "venus", sep=".")
concat("earth", "mars", "venus")

解包函數參數列表

相反的情況,就是參數已經在列表或者元組中了,但是函數調用需要分離的位置參數,這是我們就要將列表或者元組解包。舉個例子,內置函數 range 需要 start 和 stop 參數。如果這些參數不能被分別得到,我們就要用到 * 操作符進行解包來獲取列表或者元組中的參數。相似的,我們可以使用 **dict 來獲取字典中元素。

a = {'name':'xkk', 'age':18, 'hobby': 'read'}
f(**a)

lambda 表達式

小型匿名(anonymous)函數,可以使用關鍵字 lambda。這個函數返回兩個關鍵字的和:lambda a, b: a+b。lambda 函數可以在需要函數對象的地方使用。在語法上,它被限制為單個表達式。從語義上講,它們正常定義函數的語法糖(syntactic sugar),類似於內置函數定義,lambda 函數可以引用包含範圍內的變量。

>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

文檔說明

下面是一些關於說明文檔內容與格式的慣例。

第一行因該是對於對象目標簡短而準確的介紹。簡單地說,它不應該明確說明對象的名字和類型,因為這些可以通過其他方式介紹,除非對象名碰巧是描述函數操作的動詞。這一行應該以大寫字母開頭,以句號結束。(end with a period)

如果說明文檔有多行,那麼第二行應該是空白行,視覺上分割總結與其餘部分。接下來的內容,應該是一段或者更多段落,描述調用慣例和副作用(side effects)。

Python 解釋器並不會從多行字符文字中去除縮進。所以如果需要的話,處理文本的工具必須去除縮進。我們通過以下慣例實現。第一個行之後的第一個非空行確定整個文本的縮進數目。(我們不能使用第一行,因為它通常與字符串的開頭引號相鄰,所以它的縮進在文本中並不明顯)。所有行的字符串的開頭被去除與之相同的空白。缺少縮進的行不應該出現,但是所有行領先的空白應該被去除。應在擴展標籤後測試空格的有效性。

>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.

功能註釋(Function Annotations)

功能註釋關於用戶定義函數使用的類型的可選元數據信息。

註釋作為字典存儲在函數的__annotations__屬性中,對函數的其他任何部分沒有任何影響。功能註釋被參數名後面的冒號所定義,後面可以用表達式表示參數值。返回註釋有 –> 定義,後面是表達式,在參數列表與表示 def 語句結束的冒號之間。下面的例子有一個位置參數,一個關鍵字參數,並且返回值被註釋了。

def f(ham: str, eggs: str = 'eggs') -> str:
print("Annotations:", f.__annotations__)
print("Arguments:", ham, eggs)
return ham + ' and ' + eggs
f('spam')

間奏曲(intermezzo):代碼風格

  • 既然你準備寫更長、更復雜的 Python 代碼,我們很有必要來討論代碼的風格。
  • 大多數語言可以用多種風格表示,更準確地說格式化。
  • 一些的可讀性較強,使你的代碼讓別人輕易地讀出來總是一個好主意,採用一種比較好地代碼風格非常有幫助。
  • 對於 Python 來說,PEP 8 已經成為大多數項目接受的指導風格,它提出一種可讀性強,賞心悅目的代碼風格。每一個 Python 開發者都應該在某個時刻讀一下指南,下面是為你歸納出來的最重要的幾點:
  • 使用 4 個空格的縮進,而不是 tabs

四個空格在大小縮進得到了平衡(縮進短允許更大的嵌套深度,縮進長更易於閱讀),Tabs 會導致誤解,最好被省略。

  • 每一行不超過 79 個字符

這有助於用戶使用小型顯示器,並且實現了在較大顯示器並排顯示代碼文件

  • 使用空行去分隔函數與類和函數中較大的代碼塊
  • 儘可能在每一行寫下他們的註釋
  • 使用說明文檔、
  • 在操作符和之後之後使用空格,但是不直接包圍結構:a = f(1, 2) + g(3, 4)
  • 命名你的類與函數要一致,慣例是使用駝峰(CameCase)命名法命名類,小寫字母和下標命名函數與方法。總是使用 self 作為方法的第一個參數。
  • 不要使用花哨的編碼如果你的代碼要用在國際環境的話。Python 默認使用 UTF-8 編碼,或者純 ASCII 都能工作得很好。
  • 同樣,不要在鑑定符使用非 ASCII 字符,如果只有最輕微的可能會有不同語言的人閱讀與維護代碼的話。

數據結構

列表

  • list.append(x)

在列表末尾增加元素。等同於 a[len(a):] = [x]

  • list.extend(iterable)

擴展列表通過追加來自可迭代對象中的元素。等同於a[len(a):] = iterable

  • list.insert(i, x)

在給定位置插入元素。第一個參數要插入元素的索引。所以a,insert(0, x)在列表的最前面插入數據,而a.insert(len(a), x)則等同於 a.append(x)

  • list.remove(x)

刪除列表中第一次出現的值為 x 的數。如果沒有這個元素,會返回錯誤。

  • list.pop([i])

刪除列表中給定位置的元素,然後返回這個元素。如果沒有說明索引,a.pop()會刪除並返回列表中的最後一個元素。(方法中 i 周圍的方括號說明參數是可選參數,不是你應該在那個位置敲上方括號,你將會看到這個記號在 Python 參考庫頻繁地出現)

  • list.clear()

從列表中刪除全部的元素,等同於 del a[:]。a[:]相當於是列表a 的淺拷貝,刪除a[:]就是刪除列表a中所有元素。如果使用 del a,就相當於刪除列表a。

  • list.index(x[,start[,end]])

返回在以零為列表首元素索引的列表中,值為 x 的元素的索引。如果沒有該元素,會產生 ValueError。

可選參數 start 和 end 用於限定搜索的子序列的開始與結束點。被返回的索引是相對於完整序列的開頭而不是由 start 參數計算出來的。

  • list.count(x)

返回列表中 x 出現的次數。0:0

  • list.sort(key=None,reverse=False)

給列表中的元素排序。

  • list.reverse()

逆序。

  • list.copy()

返回 list 的淺拷貝,就相當於 a[:]

你可能注意到了,像insert,remove,sort之類的方法只修改 list 中的元素,不返回打印的值------他們只返回 None。這是 Python 中所有可變數據結構的設計理念。不支持 list.insert(0, 12).remove() 之類的連續操作。

作為棧使用

list 的方法使得我們非常輕鬆就可以把列表當作棧使用,棧中最後被添加的元素是第一個出棧的元素。在棧的頂部添加元素,使用 append。從棧的頂部取走元素,使用 pop() 方法

作為隊列使用

我們也可以將列表當作隊列使用,其中先進先出。但是,對於這種情況,列表並不是很高效。儘管在 list 末尾進行的 append 和 pop 操作是快速的,但是在列表的頭部插入和刪除數據是緩慢的。因為其他的元素要被一個個的移動。

為了補全隊列,使用·collections.deque,這種方法被設計快速而高效地從兩端刪除與添加元素。

from collections import deque
queue = deque([1,2,3])
queue.append(0)
queue.appendleft(8)

列表推導式

列表推導式提供了一種更簡潔的方式創建列表。這常用於創建怎樣的新列表呢?通常是列表中的元素是對其他序列或者可迭代變量的成員進行一系列操作的結果。或者在一定條件下,創建這些元素的子序列。

squares = [x**2 for x in range(10)]

更加簡潔,可讀性更強

列表推導式方括號中的表達式後面可以加上 for 語句,然後 0 個或者更多的 for 或者 if 語句。結果將會是一個新列表,通過 for 或者 if 語句限定得到的表達式

[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]

表達式是元組必須括起來

# flatten
vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem]

嵌套列表推導式

列表推導式的第一個表達式可以是任意的表達式,包括另一個列表推導式。

[[row[i] for row in matrix] for i in range(4)]
list(zip(*matrix))

del 語句

  • 有一種方式從列表中移除數值基於他的索引而不是基於他的數值
  • 這不同於 pop 方法會返回刪除的值
  • del 語句也被用於從列表中移除切片或者整個列表
  • del 也可以刪除整個變量,這時再引用變量名 a 就會產生錯誤(至少直到它被賦予新的值)

元組與序列

我們看到列表與字符串有許多共同的性質,比如索引與切片操作。他們都是序列數據類型。由於 Python 是一種不斷髮展的語言,其他序列數據類型也可能被增加。Python 中存在其他標準序列數據類型:元組。

元組是由逗號隔開的值,是不可改變量(immutable),但是可以包含列表等可變對象。

正如你看到的,輸出元組總是被括號包圍,所以嵌套元組總會被正確地解釋,儘管通常括號是不必要的(如果元組是更大表達式的一部分)。不可能給元組單個元素賦值,但是我們可以創建包含可變對象的元組。

儘管元組看上去與列表類似,但他們通常被用於不同的場合為了不同的目的。元組是不可更改的,通常包含不同類型的序列,這些序列可以通過解包或者索引來獲取。列表是可變對象,列表的元素通常是同類型的,可以通過迭代來獲取元素。(元組也可以)

一個特殊的問題是包含一個或者零個元素的元組構造:語法有特定的處理來適應這些。空元組由一對空括號組成,只有一個元素的元組,構造為:數值加上逗號(將一個值括起來是不夠的)。很醜,但是有效。

解包:t = 1, 2, 3 x, y, z = t

序列解包,適用於右邊所有序列。序列解包要求,左邊的變量個數要與右邊序列元素個數相同。注意多變量賦值只是先將序列打包再解包的過程。

集合

  • 無序性
  • 互異性
  • 成員資格測試和去重
  • 支持數學操作:並,交,補,對等差分(symmetric difference)
  • 大括號或者 set 函數用於產生集合。
  • 空集,你必須使用 set() 函數,空的大括號{}會創建空子典
  • fast membership testing : elem in set
  • a - b in a not in b
  • a ^ b in a or b but not both
  • 和列表推導式類似,集合也支持推導式

a = {x for x in ‘abracadabra’ if x not in ‘abc’ }

字典

Python 內置的另一個有用的數據類型是字典。字典有時在其他語言中被認為是“associative memories” 或者是“associative arrays”。不像序列可以被一些數字索引,字典是通過關鍵字索引,關鍵字可以是任何不可變量類型。字符串與數字總是可以作為關鍵字。元組可以被用作關鍵字,如果他們只包含字符串、數字或者元組。如果一個元組直接或者間接地包含任何可變對象,那麼他不可以用作關鍵字。你不能使用列表作為關鍵字,因為我們可以使用索引賦值、切片賦值或者 append、extend 等方式修改列表元素。

我們最好認為字典是鍵值對的集合,前提條件是關鍵字都是互異的。空的大括號創建空子典。在大括號中使用逗號隔開鍵值對可以初始化字典,這也是字典在輸出上的寫法。

字典的主要操作是存儲一些關鍵字並且基於關鍵字提取數值。我們也可以通 del 來刪除鍵值對。如果你對一個已經存在的關鍵字賦值,那麼舊的關鍵字對應的值將會被覆蓋。如果你使用不存在的關鍵字,程序就會報錯。

執行 list(d) 會按原有順序返回字典中的所有關鍵字(如果你只想排序的話,使用 sorted(d) 代替)。為了檢驗單個關鍵字是否在字典中,我們可以使用 in 這個關鍵字。

dict() 可以從其他包含鍵值對的序列構造字典。

dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
dict([['sape', 4139], ['guido', 4127], ['jack', 4098]])

字典推導式也支持任意的鍵值對錶達式

{x: x**2 for x in (2, 4, 6)}

如果關鍵字是簡單字符串,有時使用關鍵字參數來說明鍵值對更加方便。

循環技巧

當迭代字典時,我們通過 items() 方法可以同時取出關鍵字與對應值。

迭代序列,使用 enumerate(),我們可以同時得到位置索引和對應值。

為了同時迭代兩個或者更多的序列,我們可以使用 zip() 函數。

為了以相反順序迭代,我們可以使用 reversed() 函數 a[::-1]

為了按照順序迭代序列,使用 sorted() 函數。該函數返回一個新的有序序列,並保持源頭(被拷貝的序列)不變。

當你迭代一個列表時,你很有可能改變了列表,所以更簡單、更安全的方式是再新建一個列表。

Python 筆記(一)



分享到:


相關文章: