Python 3 快速入門 1 —— 資料型別與變數

WINLSR 發表於 2021-12-01
Python

本文假設你已經有一門物件導向程式語言基礎,如Java等,且希望快速瞭解並使用Python語言。本文對重點語法和資料結構以及用法進行詳細說明,同時對一些難以理解的點進行了圖解,以便大家快速入門。一些較偏的知識點在大家入門以後根據實際需要再查詢官方文件即可,學習時切忌鬍子眉毛一把抓。同時,一定要跟著示例多動手寫程式碼。學習一門新語言時推薦大家同時去刷leetcode,一來可以快速熟悉新語言的使用,二來也為今後找工作奠定基礎。推薦直接在網頁上刷leetcode,因為面試的時候一般會讓你直接在網頁編寫程式碼。leetcode刷題路徑可以按我推薦的方式去刷。以下程式碼中,以 >>>... 開頭的行是互動模式下的程式碼部分,>?開頭的行是互動模式下的輸入,其他行是輸出。python程式碼中使用 #開啟行註釋。

Python 簡介

Python作為一門指令碼語言,既可以在命令列以互動的方式進行程式編寫和執行,也可以在*.py檔案中進行編寫然後通過直譯器執行。前者適合驗證階段使用,後者更適合專案編寫。(驗證階段更推薦使用Jupyter,在編寫時可將順序執行的程式碼人為的劃分為相鄰的程式碼塊,執行時可以手動依次執行程式碼塊,當某一個程式碼塊出現錯誤時,修改了出錯的程式碼塊後無需再次重頭執行,只需從修改處程式碼塊接著執行即可。)

在學習的過程中,通過help()函式可以檢視方法、物件等的官方文件。例如檢視print函式的官方文件:

>>> help(print)
Help on built-in function print in module builtins:
print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

基本輸入輸出

輸出:print()函式:

>>> print("sum( a + b ):", 10 + 20)
sum( a + b ): 30

如上,print 函式將前面的字串和後面的計算結果通過空格拼接成字串。你也可以指定sep = "\n"引數讓其通過換行符拼接。如下:

>>> print("sum( a + b ): ", 10 + 20, sep = "\n")
sum( a + b ): 
30

輸入:input()函式,該函式預設將所有讀入的資料視為字串:

>>> a = input()
>>> print(a)
>>> print(type(a))
>? 123
123
<class 'str'="">

資料型別和變數

數值型( immutable )

int   # 整型
float # 浮點型
bool  # 布林型
complex  # 複數,需要時查詢文件即可,這裡不做講解

Python 作為動態語言,在定義變數時無需指定資料型別,執行時直譯器會自行推斷出變數型別。在PycharmPython Console選項卡(建立專案後才可以看到,在介面的最下面一行)裡可以實時顯示變數的型別:

image-20211120165750940

Python 中除法預設返回浮點數,使用雙斜槓除法//會捨去小數點後面的部分:

>>> a = 3       # 在互動模式下點選shift+enter可以實現多行程式碼編寫
... b = 4
... print(a/b) 
0.75            # / 除法返回浮點數

>>> print(a//b)
0               # // 除法捨去小數部分

>>> a = 3.0
... b = 4.0
... print(a/b)
0.75

>>> print(a//b)
0.0

Python語言中使用 **表示冪次計算,如 2 的 3 次方寫為:2 ** 3

>>> print(2 ** 3)
8

Python語言中的 bool 型別分為TrueFalse(嚴格區分大小寫),bool 型別是 int 型別的子類,且True在值上等於1False在值上等於0

>>> True == 1
True
>>> False == 0
True
>>> issubclass(bool, int)
True

提示:Python中的數值型別是引用型別而不是值型別

字串型(immutable )

str   # 字串型

Python中被''""引住的內容表示一個字串,Python中的字串與Java中的字元一樣為不可變( immutable )型別。如下:

image-20211120172106079

Python字串的強大之處在於支援下標訪問切片功能。通過下標訪問我們可以直接拿到字串中的某個字元(實際為只有一個字元構成的字串),當下標為負數時表示從右側開始:

image-20211120174342817

通過切片操作,我們可以獲取某個字串的任意子串,切片由一個區間表示,如[0: len]表示獲取下標0 - len-1的子串,即左閉右開。此外[:3]等同[0: 3][3:]等同[3: len]。由於每次切片操作都會返回一個新的字串

>>> a = 'python'

>>> print(a[:3])
pyt
>>> print(a[0: 3])
pyt

>>> print(a[3:])
hon
>>> print(a[3: len(a)])
hon

>>> print(a[:-3])  # 從開頭到倒數第三個字元表示的子串(同樣符合左閉右開)
pyt
>>> print(a[-3:])
hon

Python還支援多行字串,需要時查詢文件即可。

列表型別(mutable)

list  # 列表

Python 中的列表有點類似於 JavaList介面的實現(ArryListLinkedList),是一種高階資料結構,不同之處在於 Python 中的 list 可以儲存不同型別的資料,但通常並不會這樣使用。list 還支援切片操作,且是可變( mutable)型別。list 由中括號包住若干被逗號分隔的元素來表示,如:[1, 2, 3]

由於list是可變型別,我們可以對list進行修改:

>>> a = ['p', 'y', 't', 'h', 'o', 'n']
>>> a[1] = 'i'                  # 將下標1中的元素(引用)指向字串'i'
>>> print(a)
['p', 'i', 't', 'h', 'o', 'n']  # 第2個元素(引用)指向的字串物件由 'y' 變為 'i'

list 的切片操作返回的新列表是原列表的一個淺拷貝。淺拷貝是物件拷貝的一種方式,由於Python中只有引用型別,下面以list型別為例對引用型別的淺拷貝進行講解。下圖是列表 a 在記憶體中的示意圖:

python-list記憶體分佈

變數a是一個引用(地址),它指向記憶體中的list物件。list物件中的每一個元素(引用)又指向記憶體中的int物件。當對a執行如下切片操作時:

>>> a = [1, 2, 3, 4]
>>> b = a[:2]  # 1. b通過切片獲取a前兩個元素的淺拷貝
>>> print(a)
... print(b)
[1, 2, 3, 4]
[1, 2]
>>> b[0] = 6   # 2. 修改b[0]
>>> print(a)
... print(b) 
[1, 2, 3, 4]   # 修改b[0]並沒有影響a,為什麼呢?
[6, 2]

執行 b = a[:2]時,記憶體中會生成一個新的list物件,且b會指向這個新list物件,記憶體變化如下:

image-20211122115033653

執行b[0] = 6時,記憶體變化如下:

image-20211122115835385

由上圖容易知道這種情況下修改b並不會影響a。接著往下看:

>>> a = [1, 2, 3, [0, 0]]
>>> b = a[-2:]     # b通過切片獲取a後兩個元素的淺拷貝
>>> print(a)
... print(b)
[1, 2, 3, [0, 0]]
[3, [0, 0]]
>>> b[1][0] = 6    # 修改b[1][0]
>>> print(a)
... print(b)
[1, 2, 3, [6, 0]]  # 修改b[1][0]時,影響了a
[3, [6, 0]]

執行b = a[-2:]時,記憶體變化如下:

image-20211122134618255

執行b[1][0] = 6時,記憶體變化如下:

image-20211122154441339

由上圖容易知道,因為a[3]b[1]指向記憶體中同一個list,當通過b[1][0]修改b時會同時影響a

同時,我們還可以通過切片操作來修改列表:

>>> a = [1, 2, 3]
>>> a[1: 3] = [6, 6]  # 修改a中後兩個元素;如果這裡為a[1: 3] = [6, 6, 6, 6]會出錯嗎?動手試試
>>> print(a)
[1, 6, 6]

>>> a[:] = []         # 清空a
>>> print(a)
[]

此外,列表還支援合併與巢狀:

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b           # 合併
>>> print(c)
[1, 2, 3, 4, 5, 6]

>>> d = [a, b]          # 巢狀
>>> print(d)
[[1, 2, 3], [4, 5, 6]] 

最後,列表還提供了一系列成員方法:

  • list.append(x)
    在列表末尾新增一個元素,相當於 a[len(a):] = [x]

    >>> a = ['wjz']
    >>> a.append('lsl')
    >>> print(a)
    ['wjz', 'lsl']
    
  • list.extend(iterable)
    用可迭代物件的元素擴充套件列表。相當於 a[len(a):] = iterable

    >>> a = [1, 2, 3]
    ... b = [4, 5, 6]
    ... a.extend(b)
    ... print(a)
    [1, 2, 3, 4, 5, 6]
    
  • list.insert(i, x)
    在指定位置插入元素。第一個引數是插入元素的索引,因此,a.insert(0, x) 在列表開頭插入元素, a.insert(len(a), x) 等同於 a.append(x)

  • list.remove(x)
    從列表中刪除第一個值為 x 的元素。未找到指定元素時,觸發 ValueError 異常。

  • list.pop([i])
    刪除列表中指定位置的元素,並返回被刪除的元素。未指定位置時,a.pop() 刪除並返回列表的最後一個元素。(方法簽名中 i 兩邊的方括號表示該引數是可選的,不是要求輸入方括號。這種表示法常見於 Python 參考庫)。

    >>> a = [1, 2, 3]
    >>> a.pop()   # 預設為 pop(-1),即倒數第一個
    3
    >>> print(a)
    [1, 2]
    >>> a.pop(0)  # 刪除第一個
    1
    >>> print(a)
    [2]
    
  • list.clear()
    刪除列表裡的所有元素,相當於 del a[:]del語句可以刪除一個變數,或按切片刪除列表元素。

    # 刪除變數
    >>> a = [1, 2, 3]
    ... b = a
    ... del a
    ... print(a)  # 刪除變數a後再訪問a,報錯:未定義
    Traceback (most recent call last):
      File "<input>", line 4, in <module>
    NameError: name 'a' is not defined
    >>> print(b)  # b引用指向的列表物件還存在說明del只是刪除了a這個引用
    [1, 2, 3]
    
    # del通過切片刪除列表元素
    >>> a = [1, 2, 3]
    ... del a[:2]
    ... print(a)
    [3]
    
  • list.index(x[, start[, end]])
    返回列表中第一個值為 x 的元素的零基索引。未找到指定元素時,觸發 ValueError 異常。可選引數 startend 是切片符號,用於將搜尋限制為列表的特定子序列。返回的索引是相對於整個序列的開始計算的,而不是 start 引數。

    >>> a = [1, 2, 3, 4, 5]
    ... print(a.index(3))
    2  # a中3的下標為2
    
    >>> a = [1, 2, 3, 4, 5]
    ... print(a.index(3, 2, len(a)))  # 從第3個元素開始查詢3
    2
    
  • list.count(x)
    返回列表中元素 x 出現的次數。

  • list.sort(*, key=None, reverse=False)
    就地排序列表中的元素(要了解自定義排序引數,詳見 sorted())。該方法先忽略,入門2中有講解。

  • list.reverse()
    翻轉列表中的元素。

  • list.copy()
    返回列表的淺拷貝。相當於 a[:]

元組型別(immutable )

tuple  # 元組

元組與列表很相似,但元組為不可變型別,元組通常用來包含異質元素(型別不同)而列表通常用來包含同質元素。tuple 由小括號包住若干被逗號分隔的元素來表示,如:(1, 2, 3)。元組同樣支援切片、下標(索引)訪問。

定義一個元組:

>>> a = (1, 2, 3)
>>> b = 1, 2, 3  # 定義時省略括號也行,但不建議
>>> print(a, b)
(1, 2, 3) (1, 2, 3)

# 定義一個空元組
>>> a = ()
... print(a)
()

# 定義只有一個元素的元組
>>> a = (1,)
... print(type(a))
... print(a)
<class 'tuple'="">
(1,)

# 沒有逗號 a 就是值為 1 的int型變數
>>> a = (1)
... print(type(a))
... print(a)
<class 'int'="">
1

元組的不可變是指元組中的每一個元素(引用)的指向不能改變,以a = (1, 2, 3, 4)為例:

image-20211126164823623

上圖中被橢圓圈起來的四個指向都不能修改,但如果元組中元素(引用)指向的是一個可變型別,指向雖然不能修改,但可變型別本身是可以修改的,以a = ([1, 2], [3, 4])為例:

image-20211126170343296

上圖中被紅色橢圓圈起來的指向不能修改,被藍色橢圓圈起來的指向可以修改:

>>> a = (1, 2, 3, 4)
>>> a[0] = 6  # 不能修改a[0]的指向
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

>>> a = ([1, 2], [3, 4])
>>> a[0] = [6, 6]  # 不能修改a[0]的指向
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a[0][0] = 6    # a[0] 指向的list可以修改
>>> print(a)
([6, 2], [3, 4])

集合型別(mutable)

set  # 集合型別

集合是用來存放不含重複元素的無序容器。

定義集合:

# 通過花括號定義集合
>>> a = {1, 2, 3, 2}
... print(a)
{1, 2, 3}  # 去重

# 通過set()函式定義集合。只能傳入一個引數,且為可迭代型別。
# 會取出可迭代型別中的每一個元素為一個集合元素
>>> set("123 4")  # 傳入字串
{'3', ' ', '2', '4', '1'}
>>> set(["1", "2", "3"])  # 傳入list
{'3', '1', '2'}

# 定義空集合只能使用set()函式
>>> a = set()
... print(type(a))
... print(a)
<class 'set'="">
set()

# {}表示一個空字典
>>> a = {}
... print(type(a))
... print(a)
<class 'dict'="">
{}

字典型別(mutable)

dict  # 字典

字典型別是可變型別,類似與Java中的Map。字典型別用來儲存鍵值對,關鍵字通常是字串或數字,也可以是其他任意不可變型別。若元組直接或間接地包含了可變物件,就不能用作關鍵字。

建立字典:

# 建立空字典
>>> a = {}
>>> print(a)
{}
>>> a = dict()
>>> print(a)
{}

# 常用建立方式
# 直接在花括號中申明鍵值對
>>> a = {"chengdu": "A", "mianyang": "B", "guangyuan": "C"}
>>> print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C'}

# 關鍵字傳參的方式呼叫dict函式
>>> a = dict(chengdu="A", mianyang="B", guangyuan="C")
>>> print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C'}

# 建構函式中傳入鍵值對序列
>>> a = dict((("chengdu", "A"), ("mianyang", "B"), ("guangyuan", "C")))
>>> print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C'}

>>> a = dict([("chengdu", "A"), ("mianyang", "B"), ("guangyuan", "C")])
>>> print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C'}

>>> a = dict([["chengdu", "A"], ["mianyang", "B"], ["guangyuan", "C"]])
>>> print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C'}

對字典進行增刪改:

>>> a = dict([["chengdu", "A"], ["mianyang", "B"], ["guangyuan", "C"]])
>>> a["zigong"] = "C"  # 為不存在的key賦值即可新增新的鍵值對
... print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C', 'zigong': 'C'}

>>> del a["zigong"]    # 刪除key為 “zigong” 的鍵值對
... print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'C'}

>>> a["guangyuan"] = "F"  # 為已經存在的key賦值即可對已存在的鍵值對進行修改
... print(a)
{'chengdu': 'A', 'mianyang': 'B', 'guangyuan': 'F'}

常用高階資料結構

棧的特性是先進後出,使用list非常容易實現;使用append(e)將元素新增到棧頂,使用pop()將棧頂元素彈出:

>>> stack = [1, 2, 3]
>>> stack.append(4)  # 4入棧
... print(stack)
[1, 2, 3, 4]

>>> stack.pop()  # 4出棧
... stack.pop()  # 3出棧
... print(stack)
[1, 2]

佇列

佇列的特性是先進先出,使用list也能實現佇列,即使用append(e)將元素入隊尾,使用pop(0)使隊頭元素出隊。但pop(0)操作很費時(其他元素都必須向前移動一位),故不推薦使用list實現佇列。

實現佇列最好用 collections.deque,可以快速從兩端新增或刪除元素(雙端佇列):

  • append():從右邊入隊
  • appendleft():從左邊入隊
  • popleft():從左邊出隊
  • pop():從右邊出隊
>>> from collections import deque
... myDeque = deque([1, 2, 3])
... print(myDeque)
... myDeque.append(4)  # 4入隊尾
... print(myDeque)
... myDeque.popleft()  # 1出隊頭
... print(myDeque)
deque([1, 2, 3])
deque([1, 2, 3, 4])
deque([2, 3, 4])