1. 程式人生 > >Python 3 學習(一)—— 基礎:廖雪峰 Python 教程學習筆記

Python 3 學習(一)—— 基礎:廖雪峰 Python 教程學習筆記

文章目錄

Python教程

廖雪峰 Python 教程

值型別和引用型別

資料型別

  1. 整數
  2. 浮點數
  3. 字串
  4. 布林值(True/False)(and/or/not)
  5. 空值(None)
  6. list/tuple
  7. dict/set
  • 值型別(不可變物件):物件本身不可改變。例如:

      a = "abc"
      a.replace("a", "A")		# 輸出 Abc
      print(a)		# 輸出 abc
    

    物件本身不可變 意思是當給變數a賦值為abc後,再對變數a執行任何操作都會新分配一塊記憶體來儲存操作後得到的值,而字串本身abc無論怎樣都不會改變。

    要始終牢記的是,a是變數,而’abc’才是字串物件!有些時候,我們經常說,物件a的內容是’abc’,但其實是指,a本身是一個變數,它指向的物件的內容才是’abc’。

  • 引用型別(可變物件):物件本身可以改變。例如:

      arr = [1, 2, 3, 4, 5]
      arr[0] = 9
      print(arr)				# 輸出 [9, 2, 3, 4, 5]
    

列表和字典的基本操作

Python內建的一種資料型別是列表:listlist 是一種有序的集合,可以隨時新增和刪除其中的元素。

列表

classmates=['Michael', 'Bob', 'Tracy']
  • 獲取長度:len(classmates)
  • 追加元素到末尾:classmates.append('Adam')
  • 把元素插入到指定位置:classmates.insert(1, 'Jack')
  • 刪除末尾元素:classmates.pop()
  • 刪除指定位置的元素:classmates.pop(i)

元組

classmates = ('Michael', 'Bob', 'Tracy')

另一種有序列表叫元組:tupletuplelist 非常類似,但是 tuple 一旦初始化就不能修改。

現在,classmates 這個 tuple 不能變了,它也沒有 append()insert() 這樣的方法。其他獲取元素的方法和 list 是一樣的,你可以正常地使用 classmates[0]classmates[-1] ,但不能賦值成另外的元素。

tuple 的陷阱:定義一個只有1個元素的 tuple ,如果你這麼定義:t = (1) 定義的不是 tuple,是 1 這個數!這是因為括號 () 既可以表示 tuple,又可以表示數學公式中的小括號,這就產生了歧義,因此,Python規定,這種情況下,按小括號進行計算,計算結果自然是 1
所以,只有1個元素的 tuple 定義時必須加一個逗號,,來消除歧義:t = (1,)

“可變的” tuplet = ('a', 'b', ['A', 'B'])
tuple 所謂的“不變”是說,tuple 的每個元素,指向永遠不變。即指向 a,就不能改成指向 b,指向一個 list ,就不能改成指向其他物件,但指向的這個 list 本身是可變的!
理解了“指向不變”後,要建立一個內容也不變的 tuple 怎麼做?那就必須保證tuple 的每一個元素本身也不能變。

字典

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
  • 判斷 key 是否存在:'Thomas' in d
  • 獲取指定 keyd.get('Thomas')

如果key不存在,可以返回None,或者自己指定的value:d.get('Thomas', -1)

  • 刪除一個 keyd.pop('Bob')
  • 清空字典:d.clear()

請務必注意,dict 內部存放的順序和 key 放入的順序是沒有關係的。

list 比較,dict 有以下幾個特點:

  1. 查詢和插入的速度極快,不會隨著 key 的增加而變慢;
  2. 需要佔用大量的記憶體,記憶體浪費多。

list 相反:

  1. 查詢和插入的時間隨著元素的增加而增加;
  2. 佔用空間小,浪費記憶體很少。

所以,dict 是用空間來換取時間的一種方法。

dict 可以用在需要高速查詢的很多地方,在Python程式碼中幾乎無處不在,正確使用 dict 非常重要,需要牢記的第一條就是 dictkey必須是不可變物件

這是因為 dict 根據 key 來計算 value 的儲存位置,如果每次計算相同的 key 得出的結果不同,那 dict 內部就完全混亂了。這個通過 key 計算位置的演算法稱為 雜湊演算法(Hash)

要保證 hash 的正確性,作為 key 的物件就不能變。在Python中,字串、整數等都是不可變的,因此,可以放心地作為 key 。而 list 是可變的,就不能作為key

Set

setdict 類似,也是一組 key 的集合,但不儲存 value 。由於 key 不能重複,所以在 set 中,沒有重複的 key

要建立一個 set ,需要提供一個 list 作為輸入集合:

>>> s = set([1, 2, 3])
>>> s
	{1, 2, 3}

注意,傳入的引數 [1, 2, 3] 是一個 list ,而顯示的 {1, 2, 3} 只是告訴你這個 set 內部有1,2,3這3個元素,顯示的順序也不表示 set 是有序的。

重複元素在 set 中自動被過濾:

>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
	{1, 2, 3}
  • 新增元素:add(key) 可以重複新增,但不會有效果。
  • 刪除元素:remove(key)

set 可以看成數學意義上的無序和無重複元素的集合,因此,兩個 set 可以做數學意義上的交集、並集等操作。
setdict 的唯一區別僅在於沒有儲存對應的 value ,但是,set 的原理和 dict一樣,所以,同樣不可以放入可變物件,因為無法判斷兩個可變物件是否相等,也就無法保證 set 內部“不會有重複元素”。

函式

內建函式

Python 內建函式

Python 3 學習(二)—— 常用內建函式(網上收集整理)

定義函式

在Python中,定義一個函式要使用 def 語句,依次寫出函式名、括號、括號中的引數和冒號:,然後,在縮排塊中編寫函式體,函式的返回值用 return 語句返回。
我們以自定義一個求絕對值的 my_abs 函式為例:

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

請注意,函式體內部的語句在執行時,一旦執行到 return 時,函式就執行完畢,並將結果返回。因此,函式內部通過條件判斷和迴圈可以實現非常複雜的邏輯。
如果沒有 return 語句,函式執行完畢後也會返回結果,只是結果為 Nonereturn None可以簡寫為 return

空函式

如果想定義一個什麼事也不做的空函式,可以用 pass 語句:

def nop():
	pass

pass 語句什麼都不做,那有什麼用?實際上 pass 可以用來作為佔位符,比如現在還沒想好怎麼寫函式的程式碼,就可以先放一個 pass,讓程式碼能執行起來。
pass 還可以用在其他語句裡,比如:

if age >= 18:
	pass

缺少了 pass,程式碼執行就會有語法錯誤。

引數檢查

函式的引數:

  1. 位置引數:呼叫函式的時候傳入引數的位置是固定的

     def power(x, n):
     	s = 1
     	while n > 0:
     		n = n - 1
     		s = s * x
     	return s
    
     power(5, 2)
    
  2. 預設引數

     def power(x, n=2):
         s = 1
         while n > 0:
             n = n - 1
             s = s * x
         return s
    

    設定預設引數時,有幾點要注意:
    一是必選引數在前,預設引數在後,否則Python的直譯器會報錯(思考一下為 什麼預設引數不能放在必選引數前面);
    二是如何設定預設引數。
    當函式有多個引數時,把變化大的引數放前面,變化小的引數放後面。變化小的引數就可以作為預設引數。
    使用預設引數有什麼好處?最大的好處是能降低呼叫函式的難度。

    當不按順序提供部分預設引數時,需要把引數名寫上。

    預設引數很有用,但使用不當,也會掉坑裡。預設引數有個最大的坑,演示如下:

     def add_end(L=[]):
     	L.append('END')
     	return L
    

    當你正常呼叫時,結果似乎不錯:

     >>> add_end([1, 2, 3])
     [1, 2, 3, 'END']
     >>> add_end(['x', 'y', 'z'])
     ['x', 'y', 'z', 'END']
    

    當你使用預設引數呼叫時,一開始結果也是對的:

     >>> add_end()
     ['END']
    

    但是,再次呼叫add_end()時,結果就不對了:

     >>> add_end()
     ['END', 'END']
     >>> add_end()
     ['END', 'END', 'END']
    

    原因解釋如下:
    Python函式在定義的時候,預設引數 L的值就被計算出來了,即[],因為預設引數L也是一個變數,它指向物件[],每次呼叫該函式,如果改變了L的內容,則下次呼叫時,預設引數的內容就變了,不再是函式定義時的[]了。

    定義預設引數要牢記一點:預設引數必須指向不變物件!

    要修改上面的例子,我們可以用None這個不變物件來實現:

     def add_end(L=None):
         if L is None:
             L = []
         L.append('END')
         return L
    

    為什麼要設計str、None這樣的不變物件呢?
    因為不變物件一旦建立,物件內部的資料就不能修改,這樣就減少了由於修改資料導致的錯誤。
    此外,由於物件不變,多工環境下同時讀取物件不需要加鎖,同時讀一點問題都沒有。我們在編寫程式時,如果可以設計一個不變物件,那就儘量設計成不變物件。

  3. 可變引數:傳入的引數個數是可變的,可以是1個、2個到任意個,還可以是0個。(在引數前面加*

    例子:請計算a2 + b2 + c2 + ……。

     def calc(numbers):
         sum = 0
         for n in numbers:
             sum = sum + n * n
         return sum
    

    但是呼叫的時候,需要先組裝出一個list或tuple:

     >>> calc([1, 2, 3])
     14
     >>> calc((1, 3, 5, 7))
     84
    

    如果利用可變引數,呼叫函式的方式可以簡化成這樣:

     >>> calc(1, 2, 3)
     14
     >>> calc(1, 3, 5, 7)
     84
    

    定義可變引數和定義一個list或tuple引數相比,僅僅在引數前面加了一個*號。在函式內部,引數numbers接收到的是一個tuple,因此,函式程式碼完全不變。但是,呼叫該函式時,可以傳入任意個引數,包括0個引數:

     def calc(*numbers):
         sum = 0
         for n in numbers:
             sum = sum + n * n
         return sum
    
     >>> calc(1, 2)
     5
     >>> calc()
     0
    

    如果已經有一個list或者tuple,要呼叫一個可變引數怎麼辦?可以這樣做:

     >>> nums = [1, 2, 3]
     >>> calc(nums[0], nums[1], nums[2])
     14
    

    這種寫法當然是可行的,問題是太繁瑣,所以Python允許你在list或tuple前面加一個*號,把list或tuple的元素變成可變引數傳進去:

     >>> nums = [1, 2, 3]
     >>> calc(*nums)
     14
    

    *nums表示把nums這個list的所有元素作為可變引數傳進去。這種寫法相當有用,而且很常見。

  4. 關鍵字引數

    可變引數允許你傳入0個或任意個引數,這些可變引數在函式呼叫時自動組裝為一個tuple。而關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict。請看示例:

     def person(name, age, **kw):
     	print('name:', name, 'age:', age, 'other:', kw)
    

    函式person除了必選引數nameage外,還接受關鍵字引數kw。在呼叫該函式時,可以只傳入必選引數:

     >>> person('Michael', 30)
     name: Michael age: 30 other: {}
    

    也可以傳入任意個數的關鍵字引數:

     >>> person('Bob', 35, city='Beijing')
     name: Bob age: 35 other: {'city': 'Beijing'}
     >>> person('Adam', 45, gender='M', job='Engineer')
     name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
    
  5. 命名關鍵字引數

    對於關鍵字引數,函式的呼叫者可以傳入任意不受限制的關鍵字引數。至於到底傳入了哪些,就需要在函式內部通過 kw 檢查。

    仍以 person() 函式為例,我們希望檢查是否有 cityjob 引數:

     def person(name, age, **kw):
         if 'city' in kw:
             # 有city引數
             pass
         if 'job' in kw:
             # 有job引數
             pass
         print('name:', name, 'age:', age, 'other:', kw)
    

    但是呼叫者仍可以傳入不受限制的關鍵字引數:

     >>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)
    
    1. 如果要限制關鍵字引數的名字,就可以用命名關鍵字引數,例如,只接收 cityjob 作為關鍵字引數。這種方式定義的函式如下:
     def person(name, age, *, city, job):
     		print(name, age, city, job)
    

    和關鍵字引數 **kw 不同,命名關鍵字引數需要一個特殊分隔符 ** 後面的引數被視為命名關鍵字引數。呼叫方式如下:

     >>> person('Jack', 24, city='Beijing', job='Engineer')
     	Jack 24 Beijing Engineer
    
    1. 如果函式定義中已經有了一個可變引數,後面跟著的命名關鍵字引數就不再需要一個特殊分隔符 * 了:
     def person(name, age, *args, city, job):
     		print(name, age, args, city, job)
    

    命名關鍵字引數必須傳入引數名,這和位置引數不同。如果沒有傳入引數名,呼叫將報錯:

     >>> person('Jack', 24, 'Beijing', 'Engineer')
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: person() takes 2 positional arguments but 4 were given
    

    由於呼叫時缺少引數名 cityjob ,Python直譯器把這4個引數均視為位置引數,但 person() 函式僅接受2個位置引數。

    命名關鍵字引數可以有預設值,從而簡化呼叫:

     def person(name, age, *, city='Beijing', job):
     	 print(name, age, city, job)
    

    由於命名關鍵字引數 city 具有預設值,呼叫時,可不傳入 city 引數:

     >>> person('Jack', 24, job='Engineer')
     	Jack 24 Beijing Engineer
    

    使用命名關鍵字引數時,要特別注意,如果沒有可變引數,就必須加一個 * 作為特殊分隔符。如果缺少 * ,Python直譯器將無法識別位置引數和命名關鍵字引數:

     def person(name, age, city, job):
         # 缺少 *,city和job被視為位置引數
         pass
    
  6. 引數組合

    在Python中定義函式,可以用必選引數、預設引數、可變引數、關鍵字引數和命名關鍵字引數,這5種引數都可以組合使用。但是請注意,引數定義的順序必須是:

    • 必選引數
    • 預設引數
    • 可變引數
    • 命名關鍵字引數
    • 關鍵字引數

    比如定義一個函式,包含上述若干種引數:

     def f1(a, b, c=0, *args, **kw):
         print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
     
     def f2(a, b, c=0, *, d, **kw):
         print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
    

    在函式呼叫的時候,Python直譯器自動按照引數位置和引數名把對應的引數傳進去。

     >>> f1(1, 2)
     a = 1 b = 2 c = 0 args = () kw = {}
     >>> f1(1, 2, c=3)
     a = 1 b = 2 c = 3 args = () kw = {}
     >>> f1(1, 2, 3, 'a', 'b')
     a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
     >>> f1(1, 2, 3, 'a', 'b', x=99)
     a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
     >>> f2(1, 2, d=99, ext=None)
     a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
    

    最神奇的是通過一個tuple和dict,你也可以呼叫上述函式:

     >>> args = (1, 2, 3, 4)
     >>> kw = {'d': 99, 'x': '#'}
     >>> f1(*args, **kw)
     a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
     >>> args = (1, 2, 3)
     >>> kw = {'d': 88, 'x': '#'}
     >>> f2(*args, **kw)
     a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
    

    所以,對於任意函式,都可以通過類似func(*args, **kw)的形式呼叫它,無論它的引數是如何定義的。