Python 基礎知識自檢,離深入掌握 Python 還有多遠

一枚大果殼發表於2022-03-21

1. 模組化程式設計思想

模組化程式設計是 Python 的基本思想。初學 Python,都應該使用過小海龜、隨機、數學模組。使用模組之前,需要匯入模組,然後根據自己的問題需要使用這些模組。

Python 提供了大量的模組庫,這些模組中有 Python 語言系統自帶的、也有第三方提供的、也可以由開發者根據需要建立。

內建模組,直接拿來使用就可以。

第三方模組,需要使用 Python 自帶的安裝程式 pip(pip3 是 pip 的升級版本) 安裝。

pip3 install 模組

pip 或 pip3 是命令列程式。使用之前,需要進入 OS 的命令模式。windows 作業系統中使用 win+r 開啟執行對話方塊,輸入 cmd ,進入命令操作模式 。

命令模式下直接輸入 pip3,便可出現如下 pip3 的基本功能(前提需要安裝 Python 執行環境)。

Commands:
  install                     安裝模組
  download                    下載模組
  uninstall                   解除安裝模組
  list                        列表出安裝的模組
  show                        顯示安裝模組的資訊
  check                       驗證模組的依賴資訊
  config                      管理本地和全域性配置
  search                      從 PyPI (Python Package Index 是 Python 程式語言的軟體儲存庫) 搜尋模組
  index                       使用模組的索引檢查模組的合法性

什麼是模組化程式設計?

Python 語言系統中一個檔案就是一個模組,一個模組中可以封裝很多實用的功能程式碼,這些功能體可以以函式的形式存在,也可以以類的形式存存,模組具有完全的自我獨立性。

模組與模組之間可以在需要時相互依賴。

模組化程式設計可以大大提高程式碼的重用性,也可以無限擴充套件 Python 語言的功能。Python 經過多年的積累和沉澱,已經有諸多豐富的模組可以使用。

2. 函式

Python 語言中,函式是一等公民。

函式定義語法結構

def 函式名(函式引數):
	函式體
  • 定義函式,也就是建立一個函式必須使用 def 關鍵字。
  • 函式名是函式的唯一識別符號,也是使用函式的介面(入口)。
  • 函式引數:用來接收呼叫者傳過來的資料變數。也稱為形式引數(佔位符)。引數可以沒有,也可以有多個。
  • 函式體:函式提供的功能實現。

Python 函式的特點:

Python 既是物件導向程式語言,也是面向函式的程式語言。函式可以作為其它函式的引數,也可以作為其它函式的返回值。函式本質是一種資料型別

def han_shu():
    print(1)
print(type(han_shu))
'''
輸出結果:
<class 'function'>
'''

有些時候,稱類中函式為方法。只是語義上的區別,本質一樣。

建立函式意味著根據 function 型別建立一個 function 物件。

  1. 函式作為引數:
def a_han_shu(f):
    f()
def b_han_shu():
    print('Test')
# 函式作為引數不要有括號。    
a_han_shu(b_han_shu)
'''
輸出結果
Test
'''
  1. 函式作為返回值:
def a_han_shu():
    def b_han_shu():
        print('Test')
    return b_han_shu
# 返回的是對 b_han_shu 函式的引用
f=a_han_shu()
f()
'''
輸出結果
Test
'''

無論是使用函式作為引數還是返回值,函式不能有括號。

當函式後面有括號,意味著是執行函式中的邏輯程式碼。

  1. 使用其它模組中的函式作引數:
import turtle
def forward(f):
    f(200)
# turtle.forward 後面不能有括號
forward(turtle.forward)
'''
用自己的函式封裝小海龜的前移函式
'''
  1. 類的方法本質還是函式
class Dog:
    def __init__(self, name):
        self.name = name

    def run(self):
        print(self.name,"在 run……")

    def bark(self, f):
        f()
        print(self.name,"在 叫……")

d = Dog('小花')
d.bark(d.run)
'''
輸出結果
小花 在 run……
小花 在 叫……
'''

函式的引數形式:

  1. 位置傳遞引數:
def han_shu(num1, num2):
    return num1 - num2

#如果想得到 10-4 相減的結果,呼叫時 10,4 的順序不能顛倒。如果傳遞4,10 得到 4-10 結果
res = han_shu(10, 4)
print(res)
'''
輸出結果
6
'''
  1. 命名傳遞引數:
def han_shu(num1, num2):
    return num1 - num2

#通過引數名傳遞資料
res = han_shu(num1=10,num2=4)
print(res)
# 傳遞引數時可以顛倒順序
res = han_shu(num2=4,num1=10)
print(res)
'''
輸出結果
6
'''
  1. 預設值傳遞
def han_shu(num1, num2=4):
    return num1 - num2

#通過引數名傳遞資料
res = han_shu(10)
print(res)
'''
輸出結果
6
'''

切記:預設值引數只能放在最後。

  1. 可變長度引數
def han_shu(*num):
    return num
res = han_shu(10, 4,6,12)
print(res)
'''
輸出結果
(10, 4, 6, 12)
'''

引數前面的 * 表示可以傳遞 0 個、1 個或多個引數,函式內部會把所有傳過來的資料放在一個元組中。

def han_shu(**num):
    return num
res = han_shu(a=1,b=2,c=3)
print(res)
'''
輸出結果
{'a': 1, 'b': 2, 'c': 3}
'''

引數前面的 ** 表示可以接收任意多的以鍵、值對形式的引數。函式內部用字典的形式儲存傳過來的資料。

匿名函式

匿名函式也稱為 lambda 函式,是對函式語法的簡化。lambda 函式有如下幾個特點:

  • 沒有函式名。
  • 只能使用一次。
  • 只能有一句函式體。
  • 不能有 return 語句,預設返回唯一語句塊的計算結果。
def han_shu():
    return lambda num: num + 1
res = han_shu()
print(res(12))
'''
輸出結果
13
'''

上面的 han_shu 函式會返回一個匿名函式的引用。相當於下面的程式碼:

def han_shu():
    def f(num):
        return num + 1
    return f
res = han_shu()
print(res(12))
'''
輸出結果
'''

內建函式:

Python 中有很多內建函式、或稱為系統函式,此類函式特點:可以直接使用。

常用內建函式:

函式名 函式功能 備註
input([x]) 互動式方式獲取使用者輸入的資料 資料是字串型別
print(x) 將 x 值輸出到控制檯 不指定輸出內容時,輸出換行
pow(x,y) x 的 y 次冪,相當於 x**y
round(x,[,n]) 對 x 四捨五入,保留 n 位小數點 不指定 n 時,不保留小數位
max(x1,x2,x3,……) 返回所數列中的最大值
min(x1,x2,x3,……) 返回數列中的最小值
sum(x1,x2,x3,……) 返回數列中所有數字相加之和 引數需是可迭代型別
len( ) 返回元組、列表、集合、字串等容器物件的長度
range(start,end,step) 返回一個可迭代的物件 有index()、和count()方法
eval(x) 執行一個字串表示式 可構建動態表示式
int(x) 將 x 轉換成 int 型別資料 x 可以是字串或浮點型別
float(x) 將 x 轉換成 float 型別資料 可以是 int 或 str 型別
str(x) 將 x 轉換成 str 型別
list(x) 將一個可迭代物件轉換成列表
open() 開啟一個檔案
abs(x) 返回 x 的絕對值
type(x) 返回 x 的資料型別
ord(x) 返回字串的 unicode 編碼
chr(x) 返回 unicode 對應的字串
sorted(x) 排序操作
tuple(x) 將可迭代物件轉換成元組
set(x) 將可迭代物件轉換成集合

3. 遞迴演算法

遞迴指函式自己呼叫自己。遞迴呼叫有 2 個過程:

1、遞進過程:如有一個函式 a 。遞迴呼叫過程:第一次呼叫 a ===> 第二次呼叫 a ===>第三次呼叫 a => ……=>第 n 次呼叫 a。如果沒有任何中止條件,則會無限制推進,導致記憶體耗盡。所以,遞迴必須有一個終結條件。

2、回溯過程:回溯過程是遞進過程的逆過程。函式呼叫的特點是, a 呼叫 b 後,b 結束一定要返回到 a。在遞迴呼叫過程中,當第 n 次呼叫完成後,會進入第 n-1 次,再進入 n-2 次……一直回到第一次呼叫。

函式就是一個邏輯塊,遞迴過程也就是重複執行函式內的邏輯塊的過程,所以遞迴可以實現迴圈語法的同等效應。理論上,迴圈語法實現的操作完全可以使用遞迴方式替換,但遞迴的效能消耗要遠大於迴圈語法結構。

只有在使用迴圈語法結構不能實現或實現起來很麻煩的情況下才使用遞迴。

遞迴適合於解決,一個看起來很複雜的問題,其解決問題的關鍵點卻在一個很的子問題上時。

如求一個數字的階乘:計算 5!(5的階乘)。

  1. 遞進過程:5!=5X4! ,如果求出 4! 則 5!也就可求出,而 4!=4X3!,問題變成求 3!的階乘,而 3!=3X2!,問題變成求 2!,而 2!=2X1!,問題變成求 1!,而 1! 就是 1。到此遞進過程可以終止。

  2. 回溯過程:把 1! 的結果返回 2!,再把求得的 2!的結果返回求 3!,再把 3!的結果返回給求 4!,再把 4!的結果返回給 5!。最後得到結果。

def jc(num):
    # 遞迴終止
    if num == 1:
        return 1
    return num * jc(num - 1)
res = jc(5)
print(res)

如上面求某個數字階乘的遞迴演算法,屬於線性遞進,相對而言較容易理解。

再看一個例子:斐波拉契數列。

1,1,2,3,5,8,13,21……

從第三個數字開始,每一個數字都是前 2 個數字相加結果(第一,二位置的數字是 1)。此問題符合遞迴方案,如果想知道第 5 個位置的數字是匇,則需要知道第3,4個位置的數字是多少,如果要知道第3 個位置的則需要知道第1,2位置的數字是多少,如果要知道第 4 個位置的則要知道第 2,3位置的數字是多少。這個遞進過程是一個樹結構。

觀察遞進示意圖,發現在求第 5 個位置數字時需要知道第 3 個位置數字,求第 4 位置數字也需要求解第 3 位置數字。第 3 個位置的數字求解在整個遞進過程中至少計算了 2 次。

樹結構的遞進過程中,被重複計算是常見問題。一般會採用快取機制,對於應該計算的數字就不再計算。

不使用快取的常規遞迴演算法:

def fb(pos):
    if pos==1 or pos==2:
        return 1
    return fb(pos-1)+fb(pos-2)
res=fb(5)
print(res)
'''
輸出結果
5
'''

使用快取機制的演算法:

#快取器,位置作鍵,此位置的數字為值
cache = {}
def fb(pos):
    if pos == 1 or pos == 2:
        return 1
	# 檢視快取中是否存在此位置已經計算出來的數字
    if cache.get(pos) is not None:
        return cache.get(pos)
    # 快取中沒有,才遞進
    val = fb(pos - 2) + fb(pos - 1)
    #得到值別忘記快取
    cache[pos] = val
    return val
res = fb(5)
print(res)
'''
輸出結果
5
'''

快取機制的原理:需要某一個位置的數字時,先從快取中查詢,沒有才繼續遞進。

再看一個例子:楊輝三角

yh

楊輝三解的數字規律。每一行的第一列和最後一列為 1 ,每一行的其它列的值等於其左肩膀和右肩膀上的數字相加。

假設現在求第5行第3列的數字是多少?看如何使用遞迴方式計算。

觀察後,可看到(3,2)位置的數字被計算了兩次,如果使用遞迴方式求解楊輝三解。當行數越多時,被重複計算的值就越多,效能消耗很嚴重。

為了提升效能,儘可能使用快取機制。

常規不使用快取機制:

import time
cache = {}
def yh(row, col):
    if row == 1 or col == 1 or row == col:
        return 1
    return yh(row - 1, col - 1) + yh(row - 1, col - 2)
s = time.process_time()
res = yh(25, 5)
e = time.process_time()
print(res)
print('所使用的時間:',e - s)
'''
輸出結果
5242885
所使用的時間: 1.671875
'''

使用快取機制:

import time
def yh(row, col):
    if row == 1 or col == 1 or row == col:
        return 1
    if cache.get((row, col)) is not None:
        return cache.get((row, col))
    val = yh(row - 1, col - 1) + yh(row - 1, col - 2)
    cache[(row, col)] = val
    return val

s = time.process_time()
res = yh(25, 5)
e = time.process_time()
print(res)
print("使用時間:",e - s)
'''
輸出結果
5242885
使用時間: 0.0
'''

使用快取和不使用快取的運算時間差是很明顯的。

4. 檔案操作

使用 open("檔案路徑","模式")。

模式有 r 可讀,r+ 可讀可寫。當以 r 模式開啟時,檔案必須存在,

w 可寫,w+ 可讀可寫,當以 w 模式開啟時,檔案可以不存在,如果存在,檔案中內容會被清除。

a 可追加寫,a+ 可追加寫,可讀。當以 a 模式開啟時,檔案可以不存在,如果存在,檔案中內容不會被清除。

讀方法:

with open("d:/temp.txt","r") as f :
    # 讀所有內容
    f.read()
    # 讀一行
    f.readline()
    # 讀出所有行
    f.readlines()

寫方法:

with open("d:/temp.txt","w") as f :
    # 寫入資料
    f.write(1)
    # 把列表資料寫入
    f.writelines([1,2])
    # 使用 print 方法寫入
    print(123,34,sep=",",file=f)

5. 總結

對於 python 需要掌握的知識做了一個簡單的歸納。

相關文章