Python 基礎 (-)

banghu8816發表於2018-09-28

 

該筆記源自尚學堂,非原創

Python 單詞是“大蟒蛇”的意思。但是龜叔不是喜歡蟒蛇才起這個名字,而是正在追劇:英國電視喜劇片《蒙提·派森的飛行馬戲團》(Monty Python and the Flying Circus)。

 

使用 www.python.org 提供的 interactive shell 入門 Python

 

· 特點

  1. 可讀性強

可讀性遠比聽上去重要的多得多。一個程式會被反覆的修改,可讀性強意味著讓你可以在更短時間內學習和記憶,直接提高生產率。

  1. 簡潔,簡潔,簡潔

研究證明,程式設計師每天可編寫的有效程式碼數是有限的。完成同樣功能只用一半的程式碼,其實就是提高了一倍的生產率。

Python 是由 C 語言開發,但是不再有 C 語言中指標等複雜資料型別,Python 的簡潔性讓開發難度和程式碼幅度大幅降低,開發任務大大簡化。程式設計師再也不需要關注複雜的語法,而是關注任務本身。

 

完成這樣的螺旋線,程式碼只有幾行:

import turtle t = turtle.Pen() for x in range(360):

t.forward(x)

t.left(59)

  1. 物件導向
  2. 免費和開源
  3. 可移植性和跨平臺

Python 會被編譯成與作業系統相關的二進位制程式碼,然後再解釋執行。這種方式和 java 類似,大大提高了執行速度,也實現了跨平臺。

  1. 豐富的庫(豐富的標準庫, 多種多樣的擴充套件庫)
  2. 可擴充套件性。     可嵌入到 C 和 C++語言。     膠水式語言。

· 應用範圍

  1. 科學計算
  2. 人工智慧
  3. WEB 服務端和大型網站後端。

YouTube、gmail 等應用基於 python 開發。

  1. GUI 開發(圖形使用者介面開發)
  2. 遊戲開發
  3. 移動裝置

嵌入式裝置

  1. 系統運維
  2. 大資料
  3. 雲端計算

· 什麼時候不應該用 Python

1. Python 是解釋執行。效能較低。

因此,一些影響效能的功能可以使用 C/C++/JAVA/GO(GO 是一種新語言,寫起了像 Python,效能像 C)去開發。

不過,不用擔心 Python 直譯器會越來越快。

· 版本和相容問題解決方案

目前主要兩個版本:Python2 和 Python3

Python2:

2000 年 10 月釋出。最新版本是 2.7,已經停止更新,不會再有 2.8 以後了。預計

2020 年退出歷史舞臺。

Python3:

2008 年釋出。Python3 有了較大的提升,不相容 Python2。

相容問題解決:

1. Python3 的很多新特性也被移植到了 Python2.7,作為過渡。如果程式可以在 2.7 執行,可以通過一個名為 2to3(Python 自帶的一個指令碼)的轉換工具無縫遷移到 Python3.

2. 建議大家學習從 Python3 開始,畢竟這才是未來。

· Python 直譯器

Python 程式的執行依賴於 Python 直譯器。常用的 Python 直譯器有:

  1. CPython

使用 c 語言實現的直譯器,最常用的直譯器。通常說的直譯器指的就是它。

  1. Jython

使用 java 語言實現的直譯器。Jython 可以直接呼叫 java 類庫,適合在 java 平臺上開發

  1. IronPython

.NET 平臺上使用的直譯器。可直接呼叫.NET 平臺的類,適合.NET 平臺上開發

  1. PyPy

使用 Python 語言實現的直譯器

Python 開發入門

Python 下載安裝和配置

進入官網:www.python.org/downloads/

下載

安裝(和安裝一般軟體區別不大)

環境變數問題

勾選:“Add Python to environment variable”。 這樣就會將 Python 新增到環境變數 Path 中,我們可以在 windows 的命令列模式下執行 Python 直譯器。

 

問題:由於 dll 缺失造成安裝出錯:

 

下載 dll 修復軟體,執行修復即可,重啟計算機。

 

Python 開發環境

開發環境,英文是 IDE(Integrated Development Environment 整合開發環境)。

不要糾結於使用哪個開發環境。開發環境本質上就是對 Python 直譯器 python.exe 的封裝,核心都一樣。可以說:“開發環境 IDE,只是直譯器的一個外掛而已”,只是為了讓程式設計師更加方便程式設計,減少出錯率,尤其是拼寫錯誤。

常用的開發環境如下:

  1. IDLE
  2. Pycharm
  3. wingIDE
  4. Eclipse
  5. IPython

互動模式(指令碼 shell 模式)

  1. 進入命令列視窗,輸入:python

 

  1. >>>即為“提示符”
  2. 關閉互動視窗:

(1)    Ctrl+Z 和回車

(2)    輸入 quit()命令

(3)    直接關閉命令列視窗

  1. 中斷程式執行:ctrl+C

互動模式工作原理和 Python 處理檔案的方式一樣。除了一點:當你輸入一些值時,互動模式會自動列印輸出。Py 檔案中則必須使用 print 語句。

IDLE 開發環境使用入門

IDLE 介紹

  1. IDLE 是 Python 的官方標準開發環境,Python 安裝完後同時就安裝了 IDLE。
  2. IDLE 已經具備了 Python 開發幾乎所有功能(語法智慧提示、不同顏色顯示不同型別等等),也不需要其他配置,非常適合初學者使用。
  3. IDLE 是 Python 標準發行版內建的一個簡單小巧的 IDE,包括了互動式命令列、編輯器、偵錯程式等基本元件,足以應付大多數簡單應用。
  4. IDLE 是用純 Python 基於 Tkinter 編寫,最初的作者正是 Python 之父 Guido van

IDLE(Rossum。)

1. 互動模式

啟動 IDLE,預設就是進入互動模式。

1  編寫和執行 Python 原始檔

IDLE 常用快捷鍵

快捷鍵

 

說明

Alt+N

Alt+P

檢視歷史命令上一條、下一條

Ctrl+F6

 

重啟 shell,以前定義的變數全部失效

F1

 

開啟幫助文件

Alt+/

 

自動補全前面曾經出現過的單詞

Ctrl + [

Ctrl + ]

縮排程式碼和取消縮排

Alt+M

 

開啟模組程式碼,先選中模組,然後按下此快捷鍵,會幫你開啟改模組的 py 原始碼供瀏覽

Alt+C

 

開啟類瀏覽器,方便在原始碼檔案中的各個方法體之間切換

F5

執行程式

第一個 Python 源程式

原始碼

print("a") print("b") print("c")

將原始碼儲存到:d:/python_exec/mypy01.py

在 IDLE 中單擊 F5 或者 run-->run module 執行這個源程式。

第一個 Python 程式中需要注意的小要點:

  1. 不要在程式中,行開頭處增加空格。空格在 Python 中有縮排的含義。
  2. 符號都是英文符號,不是中文。比如:(,”

程式基本格式

  1. 恰當的空格,縮排問題

(1)    邏輯行首的空白(空格和製表符)用來決定邏輯行的縮排層次,從而用來決定語句的分組。

(2)    語句從新行的第一列開始。

(3)    縮排風格統一:每個縮排層次使用 單個製表符 或四個空格(IDE 會自動將製表符設定成 4 個空格)

Python 用縮排而不是{}表示程式塊

  1. Python 區分大小寫
  2. 註釋

(1)    行註釋

每行註釋前加#號。當直譯器看到#,則忽略這一行#後面的內容

(2)    段註釋

使用三個連續單引號(''')。當解釋看到''',則會掃描到下一個''',然後忽略他們之間的內容。

開始學習圖形化程式設計

為了讓初學者更加容易接受程式設計,我們這裡先從海龜畫圖開始講解。這樣,大家在不接

觸其他程式設計概念時,就能開始做出一些簡單的效果。提高興趣,寓教於樂。

>>> import turtle

#匯入 turtle 模組

>>> turtle.showturtle()

#顯示箭頭

>>> turtle.write("高淇")

#寫字串

>>> turtle.forward(300)

#前進 300 畫素

>>> turtle.color("red")

#畫筆顏色改為 red

>>> turtle.left(90)

>>> turtle.forward(300)

#箭頭左轉 90 度

>>> turtle.goto(0,50)

>>> turtle.goto(0,0)

#去座標(0,50)

>>> turtle.penup()

>>> turtle.goto(0,300)

#抬筆。這樣,路徑就不會畫出來

>>> turtle.pendown()

#下筆。這樣,路徑就會畫出來

>>> turtle.circle(100)

#畫圓

繪製奧運五環標記

原始碼:

import turtle turtle.width(10)

turtle.color("blue") turtle.circle(50)

turtle.color("black") turtle.penup() turtle.goto(120,0) turtle.pendown() turtle.circle(50)

turtle.color("red") turtle.penup() turtle.goto(240,0) turtle.pendown() turtle.circle(50)

turtle.color("yellow") turtle.penup() turtle.goto(60,-50) turtle.pendown() turtle.circle(50)

turtle.color("green") turtle.penup() turtle.goto(180,-50) turtle.pendown() turtle.circle(50)

執行結果:

 

本章實操作業

  1. 建立 Python 開發環境,並完成第一個 Python 程式。將整個過程使用圖文描述出來。
  2. 根據老師程式碼,完成奧運五環的繪圖程式

 

  1. 使用海龜繪圖,輸出四個矩形:

 

第2章(1) 程式設計基礎概念

Python 程式的構成

 

  1. Python 程式由模組組成。一個模組對應 python 原始檔,一般字尾名是:.py。
  2. 模組由語句組成。執行 Python 程式時,按照模組中語句的順序依次執行。

2   語句是 Python 程式的構造單元,用於建立物件、變數賦值、呼叫函式、控制語句等。

Python 檔案的建立和執行

前面使用的互動式環境,每次只能執行一條語句;為了編寫多條語句實現複雜的邏輯,本章開始我們通過建立 Python 檔案,並執行該檔案。

在 IDLE 環境中,我們可以通過 File-->new 建立 Python 檔案,並可以編輯該檔案內容。我們也可以通過 File-->save/save as 儲存檔案。一般儲存成副檔名為 py 的檔案。

需要執行編輯好的檔案,可以用快捷鍵 F5 或者點選 Run-->Run module。

程式碼的組織和縮排

很多程式語言通過字元(例如:花括號{})、關鍵字(例如:begain/end)來劃分程式碼塊。同時,在配合程式碼的縮排增加可讀性。“龜叔”設計 Python 語言時,直接通過縮排來組織程式碼塊。“縮排”成為了 Python 語法強制的規定。縮排時,幾個空格都是允許的,但是數目必須統一。我們通常採用“四個空格”表示一個縮排。

同時,也要避免將“tab 製表符”或者 tab 與空格混合的縮排風格。目前,常用的編輯器一般設定成:tab 製表符就是 4 個空格。

Python 官方推薦的 PEP-8 程式碼風格詳細說明,有興趣的同學可以參考: https://www.python.org/dev/peps/pep-0008/ 使用註釋#

註釋是程式中會被 Python 直譯器忽略的一段文字。程式設計師可以通過註釋記錄任意想寫的內容,通常是關於程式碼的說明。

Python 中的註釋只有單行註釋,使用#開始知道行結束的部分。

>>> # 註釋是個好習慣,方便自己方便他人

>>> a = [10,20,30]     #生成一個列表物件,變數 a 引用了這個變數使用\行連線符

一行程式長度是沒有限制的,但是為了可讀性更強,通常將一行比較長的程式分為多行。這是,我們可以使用\行連線符,把它放在行結束的地方。Python 直譯器仍然將它們解釋為同一行。

>>> a = [10,20,30,40,\

50,60,70,\

80,90,100]

>>> a

[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

>>> a = 'abcdefghijklmnopqrstuvwxyz'

>>> b = 'abcdefg\ hijklmn\ opqrst\ uvwxyz'

>>> a

'abcdefghijklmnopqrstuvwxyz'

>>> b

'abcdefghijklmnopqrstuvwxyz'

物件

Python 中,一切皆物件。每個物件由:標識(identity)、型別(type)、value(值)組成。

  1. 標識用於唯一標識物件,通常對應於物件在計算機記憶體中的地址。使用內建函式 id(obj) 可返回物件 obj 的標識。
  2. 型別用於表示物件儲存的“資料”的型別。型別可以限制物件的取值範圍以及可執行的操作。可以使用 type(obj)獲得物件的所屬型別。
  3. 值表示物件所儲存的資料的資訊。使用 print(obj)可以直接列印出值。物件的本質就是:一個記憶體塊,擁有特定的值,支援特定型別的相關操作。

原始碼:

>>> a = 3

>>> a

3

>>> id(3)

1531372336

>>> type(3)

<class 'int'>

>>> b = "我愛你"

>>> id(a)

1531372336

>>> type(a)

<class 'int'>

>>> print(a)

3

>>> id(b)

46806816

>>> type(b) <class 'str'>

示意圖:

 

引用

在 Python 中,變數也成為:物件的引用。因為,變數儲存的就是物件的地址。

變數通過地址引用了“物件”。

變數位於:棧記憶體(壓棧出棧等細節,後續再介紹)。

物件位於:堆記憶體。

·Python 是動態型別語言

變數不需要顯式宣告型別。根據變數引用的物件,Python 直譯器自動確定資料型別。

·Python 是強型別語言

每個物件都有資料型別,只支援該型別支援的操作。

 

識別符號

基本用法

識別符號:用於變數、函式、類、模組等的名稱。識別符號有如下特定的規則:

  1. 區分大小寫。如:sxt 和 SXT 是不同的
  2. 第一個字元必須是字母、下劃線。其後的字元是:字母、數字、下劃線
  3. 不能使用關鍵字。比如:if、or、while 等。
  4. 以雙下劃線開頭和結尾的名稱通常有特殊含義,儘量避免這種寫法。比如:__init__是類的建構函式。

【操作】使用 Python 幫助系統檢視關鍵字

 

>>> help() help> keywords

 

 

False

def

if

raise

None

del

import

return

True

elif

in

try

and

else

is

while

as

except

lambda

with

assert

finally

nonlocal

yield

 

break

for

not

 

 

class

from

or

 

 

continue

global

pass

 

 

        

注:無需刻意去背關鍵字,後面都會學習。

Python 識別符號命名規則開發中,我們通常約定俗稱遵守如下規則:

型別

規則

例子

模組和包名

全小寫字母,儘量簡單。若多個單詞之間用下劃線

math, os, sys

函式名

全小寫字母,多個單詞之間用下劃線隔開

phone, my_name

類名

首字母大寫,採用駝峰原則。多個單詞時,每個單詞第一個字母大寫,其餘部分小寫

MyPhone、MyClass、

Phone

常量名

全大寫字母,多個單詞使用下劃線隔開

SPEED、MAX_SPEED

變數和簡單賦值語句

變數的宣告和賦值

變數的宣告和賦值用於將一個變數繫結到一個物件上,格式如下:變數名 = 表示式

最簡單的表示式就是字面量。比如:a = 123 。 執行過程中,直譯器先執行右邊的表示式,生成一個代表表示式運算結果的物件;然後,將這個物件地址賦值給左邊的變數。

【操作】變數在使用前必須先被初始化(先被賦值)

>>> my_name

Traceback (most recent call last):

File "<pyshell#17>", line 1, in <module> my_name

NameError: name 'my_name' is not defined

 

變數 my_name 在被使用前未做賦值,因此報錯:’my_name’is    not  defined。刪除變數和垃圾回收機制

可以通過 del 語句刪除不在使用的變數。

【操作 55】刪除變數示例

>>> a=123

>>> del a

>>> x

Traceback (most recent call last):

File "<pyshell#20>", line 1, in <module> x

NameError: name 'x' is not defined

如果物件沒有變數引用,就會被垃圾回收器回收,清空記憶體空間。

鏈式賦值

鏈式賦值用於同一個物件賦值給多個變數。 x=y=123     相當於:x=123; y=123 系列解包賦值

系列資料賦值給對應相同個數的變數(個數必須保持一致)

>>> a,b,c=4,5,6       相當於:a=4;b=5;c=6

【操作】使用系列解包賦值實現變數交換

>>> a,b=1,2 >>> a,b=b,a

>>> print(a,b)

2 1

常量

Python 不支援常量,即沒有語法規則限制改變一個常量的值。我們只能約定常量的命名規則,以及在程式的邏輯上不對常量的值作出修改。

>>> MAX_SPEED = 120

>>> print(MAX_SPEED)

120

>>> MAX_SPEED = 140      #實際是可以改的。只能邏輯上不做修改。

>>> print(MAX_SPEED)

140

最基本內建資料型別和運算子

每個物件都有型別,python 中最基本的內建資料型別:

  1. 整型

整數,2345,10,50

  1. 浮點型

小數,3.14 或者科學計數法 314e-2

  1. 布林型

表示真假,僅包含:True、False

  1. 字串型

由字元組成的序列。 “abc”,”sxt”,“尚學堂”,”百戰程式設計師” 數字和基本運算子

Python 支援整數(如:50,520)和浮點數(如:3.14,10.0, 1.23e2),我們可以對數字做如下運算。

運算子

說明

示例

結果

+

加法

3+2

5

-

減法

30-5

25

*

乘法

3*6

18

/

浮點數除法

8/2

4.0

//

整數除法

7//2

3

%

模(取餘)

7%4

3

**

2**3

8

【操作】基本運算子的使用

>>> a = 7/2

>>> a

3.5

>>> a = 7//2

>>> a

3

>>> a = 7%2

>>> a

1

>>> 7%4 3

>>> 2**3

8

>>> 3/0

Traceback (most recent call last):

File "<pyshell#37>", line 1, in <module>

3/0

ZeroDivisionError: division by zero

>>> divmod(10,5)

(2, 0)

>>> divmod(10,3)

(3, 1)

除數為 0,會產生異常:

>>> 3/0

Traceback (most recent call last):

File "<pyshell#31>", line 1, in <module> 3/0

ZeroDivisionError: division by zero

使用 divmod()函式同時得到商和餘數:

>>> divmod(13,3)

(4, 1)

divmod()是一個函式,我們以後會詳細介紹。他返回的是一個元組(後續將會學習)。

整數

Python 中,除 10 進位制,還有其他三種進位制:

        ·0b 或 0B,二進位制   0    1

        ·0o 或 0O,八進位制   0    1    2    3    4    5    6    7

       ·0x 或 0X,十六進位制 0  1    2    3    4    5    6    7    8    9    a    b    c    d    e    f

這三種進位制可以非常方便的進行“位運算”操作。位運算知識後續將會介紹。

【操作】測試不同進位制

>>> 12

12

>>> 0b101

5

>>> 0o19

SyntaxError: invalid syntax

>>> 0o10

8

>>> 0xff

255

>>> 0xf

15

>>> 0x10 16

使用 int()實現型別轉換:

  1. 浮點數直接捨去小數部分。如:int(9.9)結果是:9
  2. 布林值 True 轉為 1,False 轉為 0。    如:int(True)結果是 1
  3. 字串符合整數格式(浮點數格式不行)則直接轉成對應整數,否則報錯。

>>> int("456")

456

>>> int("456abc")

Traceback (most recent call last):

File "<pyshell#41>", line 1, in <module> int("456abc")

ValueError: invalid literal for int() with base 10: '456abc'

>>> int("456.78")

Traceback (most recent call last):

File "<pyshell#42>", line 1, in <module>

int("456.78")

ValueError: invalid literal for int() with base 10: '456.78'

>>> 

自動轉型:

整數和浮點數混合運算時,表示式結果自動轉型成浮點數。比如:2+8.0 的結果是 10.0

整數可以有多大?

Python2 中,int 是 32 位,可以儲存從-2147483648 到 2147483647 的整數(約±

21 億)。Long 型別是 64 位,可以儲存:-2^63--2^63-1 之間的數值。

Python3 中,int 可以儲存任意大小的整數,long 被取消。我們甚至可以儲存下面的值:

>>> googol = 10**100

>>> googol

1000000000000000000000000000000000000000000000000

0000000000000000000000000000000000000000000000000

000

Googol 也是 Google 最初的名字,這也是 Google 最初的含義。

Python3 中可以做超大數的計算,而不會造成“整數溢位”,這也是 Python 特別適合科學運算的特點。

浮點數

浮點數,稱為 float。

浮點數用 ab10 形式的科學計數法表示。比如:3.14,表示成:314E-2 或者 314e-2。

這些數字在記憶體中也是按照科學計數法儲存。

型別轉換和四捨五入

  1. 類似於 int(),我們也可以使用 float()將其他型別轉化成浮點數。
  2. 整數和浮點數混合運算時,表示式結果自動轉型成浮點數。比如:2+8.0 的結果是 10.0
  3. round(value)可以返回四捨五入的值

注:但不會改變原有值,而是產生新的值

增強型賦值運算子

運算子+、-、*,/、//、**和%和賦值符=結合可以構成“增強型賦值運算子”。

a = a + 1        等價於:     a +=1

增強型賦值運算子

運算子

例子

等價

+=

a += 2

a = a + 2

-=

a -= 2

a = a-2

*=

a *= 2

a = a * 2

/=

a /= 2

a = a / 2

//=

a //= 2

a = a//2

**=

a **= 2

a = a**2

%=

a %= 2

a = a % 2

注意:“+=”中間不能加空格!時間的表示

計算機中時間的表示是從“1970 年 1 月 1 日 00:00:00”開始,以毫秒(1/1000 秒)進行計算。我們也把 1970 年這個時刻成為“unix 時間點”。

這樣,我們就把時間全部用數字來表示了。

 

python 中可以通過 time.time() 獲得當前時刻,返回的值是以秒為單位,帶微秒

(1/1000 毫秒)精度的浮點值。例如:1530167364.8566。

>>> import time

>>> b = int(time.time())

>>> b

1530168754

>>> totalMinutes = b/60

>>> totalMinutes

25502812.566666666

>>> totalMinutes = b//60

>>> totalMinutes

25502812

>>> totalHours = totalMinutes//60

>>> totalHours

425046

>>> totalDays = totalHours//24

>>> totalDays

17710

>>> totalYears = totalDays//365

>>> totalYears

48

【操作】定義多點座標_繪出折線_並計算起始點和終點距離原始碼

import turtle import math

#定義多個點的座標

x1,y1 = 100,100 x2,y2 = 100,-100 x3,y3 = -100,-100 x4,y4 = -100,100

#繪製折線

turtle.penup() turtle.goto(x1,y1) turtle.pendown() turtle.goto(x2,y2)

turtle.goto(x3,y3) turtle.goto(x4,y4)

#計算起始點和終點的距離

distance = math.sqrt((x1-x4)**2 + (y1-y4)**2) turtle.write(distance)

執行結果:                       

布林值

Python2 中沒有布林值,直接用數字 0 表示 False,用數字 1 表示 True。

Python3 中,把 True 和 False 定義成了關鍵字,但他們的本質還是 1 和 0,甚至可以和數字相加。

>>> a = True

>>> b = 3

>>> a+b

4

比較運算子

所有比較運算子返回 1 表示真,返回 0 表示假。這分別與特殊的變數 True 和 False 等價。

以下假設變數 a 為 15,變數 b 為 30:

運算子

描述

例項

==

等於 - 比較物件的值是否相等

(a == b) 返回 False。

!=

不等於 - 比較兩個物件的值是否不相等

(a != b) 返回 true.

大於 - 返回 x 是否大於 y

(a > b) 返回 False。

小於 - 返回 x 是否小於 y。

(a < b) 返回 true。

>=

大於等於 - 返回 x 是否大於等於 y。

(a >= b) 返回 False。

<=

小於等於 - 返回 x 是否小於等於 y。

(a <= b) 返回 true。

邏輯運算子

運算子

格式

說明

or

邏輯或

x or y

x 為 true,則不計算 y,直接返回 true x 為 false,則返回 y

and

邏輯與

x and y

x 為 true,則返回 y 的值 x 為 false,則不計算 y,直接返回 false

not

邏輯非

not    x

x 為 true,返回 false x 為 false,返回 true

同一運算子同一運算子用於比較兩個物件的儲存單元,實際比較的是物件的地址。

運算子

描述

is

is 是判斷兩個識別符號是不是引用同一個物件

is not

is not 是判斷兩個識別符號是不是引用不同物件

is 與 == 區別:

is 用於判斷兩個變數引用物件是否為同一個,既比較物件的地址。

== 用於判斷引用變數引用物件的值是否相等,預設呼叫物件的 __eq__()方法。

整數快取問題

Python 僅僅對比較小的整數物件進行快取(範圍為[-5, 256])快取起來,而並非是所有整數物件。需要注意的是,這僅僅是在命令列中執行,而在 Pycharm 或者儲存為檔案執行,結果是不一樣的,這是因為直譯器做了一部分優化(範圍是[-5,任意正整數])。

·總結

1、   is 比較兩個物件的 id 值是否相等,是否指向同一個記憶體地址;

2、   == 比較的是兩個物件的內容是否相等,值是否相等;

3、   小整數物件[-5,256]在全域性直譯器範圍內被放入快取供重複使用;

4、   is 運算子比 == 效率高,在變數和 None 進行比較時,應該使用 is。

【操作】同一運算子測試

>>> a = 1000

>>> b = 1000

>>> a == b

True

>>> a is b

False

>>> id(a)

46764560

>>> id(b)

46765216

>>> c = 10

>>> d = 10

>>> c is d

True

>>> id(c)

1388831648

>>> id(d)

1388831648

基本運算子

我們在前面講解了“+”、“-”、“*”、“/”、“//”、“%”等運算子,這裡我們繼續講解一些其他運算子,並進行學習和測試。

運算子

說明

and ,      or    ,      not

布林與、布林或、布林非

is    ,    is    not

同一性判斷,判斷是否為同一個物件

<,<=,>,>=,!=,==

比較值是否相當,可以連用

|       ^           &

按位或,按位異或、按位與

<<, >>

移位

~

按位翻轉

+,-,*,/,//,%

加,減,乘,浮點除、整數除、取餘

**

冪運算

1. 比較運算子可以連用,並且含義和我們日常使用完全一致。

>>> a = 4

          >>> 3<a<10   #關係運算子可以連用

True

2. 位操作

>>> a = 0b11001

>>> b = 0b01000

>>> c = a|b

          >>> bin(c)         #bin()可以將數字轉成二進位制表示

'0b11001'

>>> bin(c&b)

'0b1000'

>>> bin(c^b)

'0b10001'

>>> a = 3

          >>> a<<2     #左移 1 位相當於乘以 2.左移 2 位,相當於乘以 4

12

>>> a = 8

          >>> a>>1         #右移 1 位相當於除以 2.

3. 加法操作

          (1)   數字相加                     3+2    ==> 5

          (2)  字串拼接

“3”+“2”==> “32”

          (3)  列表、元組等合併

4. 乘法操作

[10,20,30]+[5,10,100] ==>[10,20,30,5,10,100]

          (1)  數字相乘

3*2         ==>      6

          (2)  字串複製

“sxt”*3           ==>         ”sxtsxtsxt”

          (3)  列表、元組等複製

[10,20,30]*3 ==> [10,20,30,10,20,30,10,20,30]

複合賦值運算子複合賦值可以讓程式更加精煉,提高效率。

運算子

描述

示例

等價於

+=

加法賦值字串拼接

sum += n a       += “sxt”

sum = sum + n a =       a + “sxt”

-=

減法賦值

num1        -= n

num = num - n

*=

乘法賦值

a *= b

a = a * b

/=

浮點除賦值

a/=b

a = a / b

//=

整數除賦值

a//=b

a = a//b

%=

取餘賦值

a%=b

a = a % b

**=

冪運算賦值

a**=2

a = a**2

<<=

左移賦值

a<<=2

a = a<<2

>>=

右移賦值

a>>=2

a = a>>2

&=

按位與賦值

a&=b

a = a&b

|=

按位或賦值

a|=b

a=a|b

^=

按位異或賦值

a^=b

a = a^b

注:與 C 和 JAVA 不一樣,Python 不支援自增(++)和自減(--) 運算子優先順序問題如下優先順序,從高到低。

運算子

描述

**

指數 (最高優先順序)

~

按位翻轉

* / % //

乘,除,取模和取整除

+ -

加法減法

>> <<

右移,左移運算子

&

位 'AND'

^ |

位運算子

<= < > >=

比較運算子

<> == !=

等於運算子

= %= /= //= -= += *= **=

賦值運算子

is is not

身份運算子

in not in

成員運算子

not or and

邏輯運算子

實際使用中,記住如下簡單的規則即可,複雜的表示式一定要使用小括號組織。

  1. 乘除優先加減
  2. 位運算和算術運算>比較運算子>賦值運算子>邏輯運算子

【操作】使用 python 表示數學式: 510x13(y1)(ab) 9(5 12x)

                                                                                 5                    x                   x        y

(5+10*x)/5-13*(y-1)*(a+b)/x+9*(5/x+(12+x)/y)

第2章(2) 字串

字串基本特點

很多人初學程式設計時,總是擔心自己數學不行,潛意識裡認為數學好才能程式設計。實際上,大多數程式設計師打交道最多的是“字串”而不是“數字”。因為,程式設計是用來解決現實問題的,因此邏輯思維的重要性遠遠超過數學能力。

字串的本質是:字元序列。Python 的字串是不可變的,我們無法對原字串做任

何修改。但,可以將字串的一部分複製到新建立的字串,達到“看起來修改”的效果。

Python 不支援單字元型別,單字元也是作為一個字串使用的。

字串的編碼

Python3 直接支援 Unicode,可以表示世界上任何書面語言的字元。Python3 的字元預設就是 16 位 Unicode 編碼,ASCII 碼是 Unicode 編碼的子集。

使用內建函式 ord()可以把字元轉換成對應的 Unicode 碼;使用內建函式 chr()可以把十進位制數字轉換成對應的字元。

>>> ord('A')

65

>>> ord('高')

39640

>>> chr(66)

'B'

>>> ord('淇')

28103

引號建立字串

我們可以通過單引號或雙引號建立字串。例如:a=’abc’;         b=”sxt”

使用兩種引號的好處是可以建立本身就包含引號的字串,而不用使用轉義字元。例如:

>>> a = "I'm a teacher!"

>>> print(a) I'm a teacher!

>>> b = 'my_name is "TOM"'

>>> print(b) my_name is "TOM"

連續三個單引號或三個雙引號,可以幫助我們建立多行字串。例如:

>>> resume = ''' name="gaoqi"

company="sxt"       age=18

lover="Tom"'''

>>> print(resume) name="gaoqi"

company="sxt"    age=18 lover="Tom"

空字串和 len()函式

Python 允許空字串的存在,不包含任何字元且長度為 0。例如:

>>> c = ''

>>> len(c)

0

len()用於計算字串含有多少字元。例如:

>>> d = 'abc 尚學堂'

>>> len(d)

6

轉義字元

我們可以使用“\+特殊字元”,實現某些難以用字元表示的效果。比如:換行等。常見的轉義字元有這些:

轉義字元

描述

 

\(在行尾時)

 

 

續行符

 

\\

反斜槓符號

\'

單引號

\"

雙引號

\b

退格(Backspace)

\n

換行

\t

橫向製表符

\r

回車

【操作】測試轉義字元的使用

>>> a = 'I\nlove\nU'

>>> a

'I\nlove\nU'

>>> print(a)

I

love

U

>>> print('aaabb\ cccddd') aaabbcccddd

字串拼接

  1. 可以使用+將多個字串拼接起來。例如:’aa’+ ’bb’ ==>’aabb’。

(1)    如果+兩邊都是字串,則拼接。

(2)    如果+兩邊都是數字,則加法運算。

(3)    如果+兩邊型別不同,則丟擲異常。

  1. 可以將多個字面字串直接放到一起實現拼接。例如:’aa’’bb’==>’aabb’

【操作】字串拼接操作

>>> a = 'sxt'+'gaoqi'

>>> a

'sxtgaoqi'

>>> b = 'sxt''gaoqi'

>>> b

'sxtgaoqi'

字串複製

使用*可以實現字串複製。

【操作】字串複製操作

>>> a = 'Sxt'*3

>>> a

'SxtSxtSxt'

不換行列印

我們前面呼叫 print 時,會自動列印一個換行符。有時,我們不想換行,不想自動新增換行

符。我們可以自己通過引數 end = “任意字串”。實現末尾新增任何內容:

建立原始檔 mypy_06.py:

print("sxt",end=' ') print("sxt",end='##') print("sxt")

執行結果:

sxt sxt##sxt

從控制檯讀取字串

我們可以使用 input()從控制檯讀取鍵盤輸入的內容。

>>> myname = input("請輸入名字:")

請輸入名字:高淇

>>> myname '高淇'

str()實現數字轉型字串

str()可以幫助我們將其他資料型別轉換為字串。例如:

str(5.20) ==> ‘5.20’                       str(3.14e2)==>’314.0’                        str(True) ==> ‘True’

當我們呼叫 print()函式時,直譯器自動呼叫了 str()將非字串的物件轉成了字串。我們在物件導向章節中詳細講解這部分內容。

使用[]提取字元

字串的本質就是字元序列,我們可以通過在字串後面新增[],在[]裡面指定偏移量,

可以提取該位置的單個字元。

正向搜尋:最左側第一個字元,偏移量是 0,第二個偏移量是 1,以此類推。直到 len(str)-1

為止。

反向搜尋:

最右側第一個字元,偏移量是-1,倒數第二個偏移量是-2,以此類推,直到-len(str)

為止。

【操作】使用[]提取字串中的字元

>>> a = 'abcdefghijklmnopqrstuvwxyz'

>>> a

'abcdefghijklmnopqrstuvwxyz'

>>> a[0]

'a'

>>> a[3]

'd'

>>> a[26-1]

'z'

>>> a[-1]

'z'

>>> a[-26]

'a'

>>> a[-30]

Traceback (most recent call last):

File "<pyshell#91>", line 1, in <module> a[-30]

IndexError: string index out of range

replace()實現字串替換

字串是“不可改變”的,我們通過[]可以獲取字串指定位置的字元,但是我們不能改變字串。我們嘗試改變字串中某個字元,發現報錯了:

>>> a = 'abcdefghijklmnopqrstuvwxyz'

>>> a

'abcdefghijklmnopqrstuvwxyz'

>>> a[3]='高'

Traceback (most recent call last):

File "<pyshell#94>", line 1, in <module> a[3]='高'

TypeError: 'str' object does not support item assignment

字串不可改變。但是,我們確實有時候需要替換某些字元。這時,只能通過建立新的字串來實現。

>>> a = 'abcdefghijklmnopqrstuvwxyz'

>>> a

'abcdefghijklmnopqrstuvwxyz'

>>> a = a.replace('c','高')

'ab 高 defghijklmnopqrstuvwxyz'

整個過程中,實際上我們是建立了新的字串物件,並指向了變數 a,而不是修改了以前的字串。 記憶體圖如下:

 

字串切片 slice 操作

切片 slice 操作可以讓我們快速的提取子字串。標準格式為:

[起始偏移量 start:終止偏移量 end:步長 step]

典型操作(三個量為正數的情況)如下:

操作和說明

示例

結果

[:]     提取整個字串

“abcdef”[:]

“abcdef”

[start:]從 start 索引開始到結尾

“abcdef”[2:]

“cdef”

[:end]從頭開始知道 end-1

“abcdef”[:2]

“ab”

[start:end]從 start 到 end-1

“abcdef”[2:4]

“cd”

[start:end:step]從 start 提取到 end-1,步長是 step

“abcdef”[1:5:2]

“bd”

其他操作(三個量為負數)的情況:

示例

說明

結果

"abcdefghijklmnopqrstuv wxyz"[-3:]

倒數三個

“xyz”

"abcdefghijklmnopqrstuv wxyz"[-8:-3]

倒數第八個到倒數第三個(包頭不包尾)

'stuvw'

"abcdefghijklmnopqrstuv wxyz"[::-1]

步長為負,從右到左反向提取

'zyxwvutsrqpon mlkjihgfedcba'

切片操作時,起始偏移量和終止偏移量不在[0,字串長度-1]這個範圍,也不會報錯。起始

偏移量小於 0 則會當做 0,終止偏移量大於“長度-1”會被當成-1。例如:

>>> "abcdefg"[3:50]

'defg'

我們發現正常輸出了結果,沒有報錯。

【操作】

1. 將”to be or not to be”字串倒序輸出 2. 將”sxtsxtsxtsxtsxt”字串中所有的 s 輸出

split()分割和 join()合併

split()可以基於指定分隔符將字串分隔成多個子字串(儲存到列表中)。如果不指定分隔符,則預設使用空白字元(換行符/空格/製表符)。示例程式碼如下:

>>> a = "to be or not to be"

>>> a.split()

['to', 'be', 'or', 'not', 'to', 'be']

>>> a.split('be')

['to ', ' or not to ', '']

join()的作用和 split()作用剛好相反,用於將一系列子字串連線起來。示例程式碼如下:

>>> a = ['sxt','sxt100','sxt200']

>>> '*'.join(a)

'sxt*sxt100*sxt200'

拼接字串要點:

使用字串拼接符+,會生成新的字串物件,因此不推薦使用+來拼接字串。推薦

使用 join 函式,因為 join 函式在拼接字串之前會計算所有字串的長度,然後逐一拷貝,僅新建一次物件。

【操作】測試+拼接符和 join(),不同的效率 (mypy_07.py)

import time

time01 = time.time()      #起始時刻

a = "" for i in range(1000000):

a += "sxt" time02 = time.time() #終止時刻

print("運算時間:"+str(time02-time01))

time03 = time.time()   #起始時刻 li = [] for i in range(1000000):

li.append("sxt") a = "".join(li)

time04 = time.time()       #終止時刻

print("運算時間:"+str(time04-time03))

字串駐留機制和字串比較

字串駐留:僅儲存一份相同且不可變字串的方法,不同的值被存放在字串駐留池中。 Python 支援字串駐留機制,對於符合識別符號規則的字串(僅包含下劃線(_)、字母和數字)會啟用字串駐留機制駐留機制。

>>> a = "abd_33"

>>> b = "abd_33"

>>> a is b

True

>>> c = "dd#"

>>> d = "dd#"

>>> c is d

False

>>> str1 = "aa"

>>> str2 = "bb"

>>> str1+str2      is "aabb"

False

>>> str1+str2 == "aabb"

True

字串比較和同一性

我們可以直接使用==,!=對字串進行比較,是否含有相同的字元。

我們使用 is / not is,判斷兩個物件是否同一個物件。比較的是物件的地址,即 id(obj1)是否和 id(obj2)相等。

成員操作符

in /not in 關鍵字,判斷某個字元(子字串)是否存在於字串中。

字串常用方法彙總

字串有很多常用的方法,我們需要熟悉。我們通過表格將這些方法彙總起來,方便大家查閱。希望大家針對每個方法都做一次測試。

常用查詢方法

我們以一段文字作為測試: a='''我是高淇,今年 18 歲了,我在北京尚學堂科技上班。我的兒子叫高洛希,他 6 歲了。我是一個程式設計教育的普及者,希望影響 6000 萬學習程式設計的中國人。我兒子現在也開始學習程式設計,希望他 18 歲的時候可以超過我'''

方法和使用示例

說明

結果

len(a)

字串長度

96

a.startswith('我是高淇')

以指定字串開頭

True

a.endswith('過我')

以指定字串結尾

True

a.find('高')

第一次出現指定字串的位置

2

a.rfind('高')

最後一次出現指定字串的位置

29

a.count("程式設計")

指定字串出現了幾次

3

a.isalnum()

所有字元全是字母或數字

False

去除首尾資訊

我們可以通過 strip()去除字串首尾指定資訊。通過 lstrip()去除字串左邊指定資訊, rstrip()去除字串右邊指定資訊。【操作】去除字串首尾資訊

>>> "*s*x*t*".strip("*")

's*x*t'

>>> "*s*x*t*".lstrip("*")

's*x*t*'

>>> "*s*x*t*".rstrip("*")

'*s*x*t'

>>> "     sxt    ".strip()

'sxt'

大小寫轉換

程式設計中關於字串大小寫轉換的情況,經常遇到。我們將相關方法彙總到這裡。為了方便學習,先設定一個測試變數:

                                           a = "gaoqi    love     programming, love     SXT"

示例

說明

結果

a.capitalize()

產生新的字串,首字母大寫

'Gaoqi      love     programming, love       sxt'

a.title()

產生新的字串,每個單詞都首字母大寫

'Gaoqi      Love     Programming, Love     Sxt'

a.upper()

產生新的字串,所有字元全轉成大寫

'GAOQI       LOVE      PROGRAMMING, LOVE

SXT'

a.lower()

產生新的字串,所有字元全轉成小寫

'gaoqi      love     programming, love        sxt'

a.swapcase()

產生新的,所有字母大小寫轉換

'GAOQI

sxt'

LOVE

PROGRAMMING, LOVE

格式排版

center()、ljust()、rjust()這三個函式用於對字串實現排版。示例如下:

>>> a="SXT"

>>> a.center(10,"*")

'***SXT****'

>>> a.center(10)

'      SXT        '

>>> a.ljust(10,"*") 'SXT*******' 其他方法

1. isalnum() 是否為字母或數字 2. isalpha() 檢測字串是否只由字母組成(含漢字)。

  1. isdigit()     檢測字串是否只由數字組成。
  2. isspace() 檢測是否為空白符
  3. isupper() 是否為大寫字母
  4. islower() 是否為小寫字母

>>> "sxt100".isalnum()

True

>>> "sxt 尚學堂".isalpha()

True

>>> "234.3".isdigit()

False

>>> "23423".isdigit()

True

>>> "aB".isupper()

False

>>> "A".isupper()

True

>>> "\t\n".isspace()

True

字串的格式化

format()基本用法

Python2.6 開始,新增了一種格式化字串的函式 str.format(),它增強了字串格式化的功能。

基本語法是通過 {} 和 : 來代替以前的 % 。

format 函式可以接受不限個引數,位置可以不按順序。

我們通過示例進行格式化的學習。

>>> a = "名字是:{0},年齡是:{1}"

>>> a.format("高淇",18)

'名字是:高淇,年齡是:18'

>>> a.format("高希希",6)

'名字是:高希希,年齡是:6'

>>> b = "名字是:{0},年齡是{1}。{0}是個好小夥"

>>> b.format("高淇",18)

'名字是:高淇,年齡是 18。高淇是個好小夥'

>>> c = "名字是{name},年齡是{age}"

>>> c.format(age=19,name='高淇')

'名字是高淇,年齡是 19'

我們可以通過{索引}/{引數名},直接對映引數值,實現對字串的格式化,非常方便。

填充與對齊

填充常跟對齊一起使用

^、<、>分別是居中、左對齊、右對齊,後面頻寬度

:號後面帶填充的字元,只能是一個字元,不指定的話預設是用空格填充

>>> "{:*>8}".format("245")

'*****245'

>>> "我是{0},我喜歡數字{1:*^8}".format("高淇","666")

'我是高淇,我喜歡數字**666***'

數字格式化

浮點數通過 f,整數通過 d 進行需要的格式化。案例如下:

>>> a = "我是{0},我的存款有{1:.2f}"

>>> a.format("高淇",3888.234342)

'我是高淇,我的存款有 3888.23'

其他格式,供大家參考:

數字

格式

輸出

描述

3.1415926

{:.2f}

3.14

保留小數點後兩位

3.1415926

{:+.2f}

3.14

帶符號保留小數點後兩位

2.71828

{:.0f}

3

不帶小數

5

{:0>2d}

05

數字補零 (填充左邊, 寬度為 2)

5

{:x<4d}

5xxx

數字補 x (填充右邊, 寬度為 4)

10

{:x<4d}

10xx

數字補 x (填充右邊, 寬度為 4)

1000000

{:,}

1,000,000

以逗號分隔的數字格式

0.25

{:.2%}

25.00%

百分比格式

1000000000

{:.2e}

1.00E+09

指數記法

13

{:10d}

13

右對齊 (預設, 寬度為 10)

13

{:<10d}

13

左對齊 (寬度為 10)

13

{:^10d}

13

中間對齊 (寬度為 10)

可變字串

在 Python 中,字串屬於不可變物件,不支援原地修改,如果需要修改其中的值,智慧建立新的字串物件。但是,經常我們確實需要原地修改字串,可以使用 io.StringIO 物件或 array 模組。

>>> import io

>>> s = "hello, sxt"

>>> sio = io.StringIO(s)

>>> sio

<_io.StringIO object at 0x02F462B0>

>>> sio.getvalue()

'hello, sxt'

>>> sio.seek(7)

7

>>> sio.write("g")

1

>>> sio.getvalue() 'hello, gxt'

本章實操作業

  1. 使用 python 表示數學式: 510x13(y1)(ab) 9(5 12x)

                                                                     5                    x                   x        y

  1. 從控制檯輸入使用者的月薪,進行運算計算出年薪。列印輸出使用者的年薪
  2. 使用字串複製,用計算機列印出“愛你一百遍”,列印 100 次
  3. 將”to be or not to be”字串倒序輸出
  4. 將”sxtsxtsxtsxtsxt”字串中所有的 s 輸出
  5. 判斷如下輸出結果,並文字解釋原因:

>>> a = "abd_33"

>>> b = "abd_33"

>>> c = "dd#"

>>> d = "dd#"

>>> a is b    #輸出 true   or    false? >>> c is d  #輸出 true   or       false?

  1. 寫出如下程式碼列印的結果:

>>> c = "名字是{name},年齡是{age}"

>>> c.format(age=19,name='高淇')

第3章 序列

序列是一種資料儲存方式,用來儲存一系列的資料。在記憶體中,序列就是一塊用來存放多個值的連續的記憶體空間。比如一個整數序列[10,20,30,40],可以這樣示意表示:

 

由於 Python3 中一切皆物件,在記憶體中實際是按照如下方式儲存的: a = [10,20,30,40]

 

從圖示中,我們可以看出序列中儲存的是整數物件的地址,而不是整數物件的值。python 中常用的序列結構有:

字串、列表、元組、字典、集合

我們上一章學習的字串就是一種序列。關於字串裡面很多操作,在這一章中仍然會用到,大家一定會感覺非常熟悉。

本章內容,我們必須非常熟悉。無論是在學習還是工作中,序列都是每天都會用到的技術,可以非常方便的幫助我們進行資料儲存的操作。

列表簡介

列表:用於儲存任意數目、任意型別的資料集合。

列表是內建可變序列,是包含多個元素的有序連續的記憶體空間。列表定義的標準語法格式: a = [10,20,30,40] 其中,10,20,30,40 這些稱為:列表 a 的元素。

列表中的元素可以各不相同,可以是任意型別。比如:

a = [10,20,'abc',True] 列表物件的常用方法彙總如下,方便大家學習和查閱。

方法

要點

描述

list.append(x)

增加元素

將元素 x 增加到列表 list 尾部

list.extend(aList)

增加元素

將列表 alist 所有元素加到列表 list 尾部

list.insert(index,x)

增加元素

在列表 list 指定位置 index 處插入元素 x

list.remove(x)

刪除元素

在列表 list 中刪除首次出現的指定元素 x

list.pop([index])

刪除元素

刪除並返回列表 list 指定為止 index 處的元素,預設是最後一個元素

list.clear()

刪除所有元素

刪除列表所有元素,並不是刪除列表物件

list.index(x)

訪問元素

返回第一個 x 的索引位置,若不存在 x 元素丟擲異常

list.count(x)

計數

返回指定元素 x 在列表 list 中出現的次數

len(list)

列表長度

返回列表中包含元素的個數

list.reverse()

翻轉列表

所有元素原地翻轉

list.sort()

排序

所有元素原地排序

list.copy()

淺拷貝

返回列表物件的淺拷貝

Python 的列表大小可變,根據需要隨時增加或縮小。

字串和列表都是序列型別,一個字串是一個字元序列,一個列表是任何元素的序列。我們前面學習的很多字串的方法,在列表中也有類似的用法,幾乎一模一樣。

列表的建立基本語法[]建立

>>> a = [10,20,'gaoqi','sxt']

>>> a = []       #建立一個空的列表物件

list()建立

使用 list()可以將任何可迭代的資料轉化成列表。

>>> a = list()    #建立一個空的列表物件

>>> a = list(range(10))

>>> a

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> a = list("gaoqi,sxt")

>>> a

['g', 'a', 'o', 'q', 'i', ',', 's', 'x', 't']

range()建立整數列表

range()可以幫助我們非常方便的建立整數列表,這在開發中及其有用。語法格式為:

range([start,] end [,step])

start 引數:可選,表示起始數字。預設是 0 end 引數:必選,表示結尾數字。 step 引數:可選,表示步長,預設為 1

python3 中 range()返回的是一個 range 物件,而不是列表。我們需要通過 list()方法將其轉換成列表物件。典型示例如下:

>>> list(range(3,15,2))

[3, 5, 7, 9, 11, 13]

>>> list(range(15,3,-1))

[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4]

>>> list(range(3,-10,-1))

[3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

推導式生成列表(簡介一下,重點在 for 迴圈後講)

使用列表推導式可以非常方便的建立列表,在開發中經常使用。但是,由於涉及到 for 迴圈和 if 語句。在此,僅做基本介紹。在我們控制語句後面,會詳細講解更多列表推導式的細節。

>>> a = [x*2     for    x    in range(5)]       #迴圈建立多個元素

>>> a

[0, 2, 4, 6, 8]

>>> a = [x*2 for x in range(100) if x%9==0]             #通過 if 過濾元素

>>> a

[0, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198] 列表元素的增加和刪除

當列表增加和刪除元素時,列表會自動進行記憶體管理,大大減少了程式設計師的負擔。但這個特點涉及列表元素的大量移動,效率較低。除非必要,我們一般只在列表的尾部新增元素或刪除元素,這會大大提高列表的操作效率。

append()方法

原地修改列表物件,是真正的列表尾部新增新的元素,速度最快,推薦使用。

>>> a = [20,40]

>>> a.append(80)

>>> a

[20, 40, 80]

+運算子操作

並不是真正的尾部新增元素,而是建立新的列表物件;將原列表的元素和新列表的元素依次複製到新的列表物件中。這樣,會涉及大量的複製操作,對於操作大量元素不建議使用。 >>> a = [20,40]

>>> id(a)

46016072

>>> a = a+[50]

>>> id(a) 46015432

通過如上測試,我們發現變數 a 的地址發生了變化。也就是建立了新的列表物件。

extend()方法

將目標列表的所有元素新增到本列表的尾部,屬於原地操作,不建立新的列表物件。

>>> a = [20,40]

>>> id(a)

46016072

>>> a.extend([50,60])

>>> id(a) 46016072

insert()插入元素

使用 insert()方法可以將指定的元素插入到列表物件的任意制定位置。這樣會讓插入位置後面所有的元素進行移動,會影響處理速度。涉及大量元素時,儘量避免使用。類似發生這種移動的函式還有:remove()、pop()、del(),它們在刪除非尾部元素時也會發生操作位置後面元素的移動。

>>> a = [10,20,30]

>>> a.insert(2,100)

>>> a

[10, 20, 100, 30] 乘法擴充套件

使用乘法擴充套件列表,生成一個新列表,新列表元素時原列表元素的多次重複。

>>> a = ['sxt',100]

>>> b = a*3

>>> a

['sxt', 100]

>>> b

['sxt', 100, 'sxt', 100, 'sxt', 100]

適用於乘法操作的,還有:字串、元組。例如:

>>> c = 'sxt'

>>> d = c*3

>>> c

'sxt'

>>> d

'sxtsxtsxt'

列表元素的刪除

del 刪除

刪除列表指定位置的元素。

>>> a = [100,200,888,300,400]

>>> del a[1]

>>> a

[100,200,300,400]

 

pop()方法

pop()刪除並返回指定位置元素,如果未指定位置則預設操作列表最後一個元素。

>>> a = [10,20,30,40,50]

>>> a.pop()

50

>>> a

[10, 20, 30, 40]

>>> a.pop(1)

20

>>> a

[10, 30, 40]

remove()方法

刪除首次出現的指定元素,若不存在該元素丟擲異常。

>>> a = [10,20,30,40,50,20,30,20,30]

>>> a.remove(20)

>>> a

[10, 30, 40, 50, 20, 30, 20, 30]

>>> a.remove(100)

Traceback (most recent call last):

File "<pyshell#208>", line 1, in <module>

a.remove(100)

ValueError: list.remove(x): x not in list

列表元素訪問和計數通過索引直接訪問元素

我們可以通過索引直接訪問元素。索引的區間在[0, 列表長度-1]這個範圍。超過這個範圍則會丟擲異常。

>>> a = [10,20,30,40,50,20,30,20,30]

>>> a[2]

30

>>> a[10]

Traceback (most recent call last):

File "<pyshell#211>", line 1, in <module> a[10]

IndexError: list index out of range

index()獲得指定元素在列表中首次出現的索引

index()可以獲取指定元素首次出現的索引位置。語法是:index(value,[start,[end]])。其中, start 和 end 指定了搜尋的範圍。

>>> a = [10,20,30,40,50,20,30,20,30]

>>> a.index(20)

1

>>> a.index(20,3)

5

>>> a.index(20,3)     #從索引位置 3 開始往後搜尋的第一個 20

5

>>> a.index(30,5,7) #從索引位置 5 到 7 這個區間,第一次出現 30 元素的位置

6

count()獲得指定元素在列表中出現的次數

count()可以返回指定元素在列表中出現的次數。

>>> a = [10,20,30,40,50,20,30,20,30]

>>> a.count(20)

3

len()返回列表長度

len()返回列表長度,即列表中包含元素的個數。

>>> a = [10,20,30]

>>> len(a)

3

成員資格判斷

判斷列表中是否存在指定的元素,我們可以使用 count()方法,返回 0 則表示不存在,返回大於 0 則表示存在。但是,一般我們會使用更加簡潔的 in 關鍵字來判斷,直接返回 True 或 False。

>>> a = [10,20,30,40,50,20,30,20,30]

>>> 20 in a

True

>>> 100 not in a

True

>>> 30 not in a

False

切片操作

我們在前面學習字串時,學習過字串的切片操作,對於列表的切片操作和字串類似。切片是 Python 序列及其重要的操作,適用於列表、元組、字串等等。切片的格式如下:

切片 slice 操作可以讓我們快速提取子列表或修改。標準格式為:

[起始偏移量 start:終止偏移量 end[:步長 step]]

注:當步長省略時順便可以省略第二個冒號

典型操作(三個量為正數的情況)如下:

操作和說明

示例

結果

[:]     提取整個列表

[10,20,30][:]

[10,20,30]

[start:]從 start 索引開始到

結尾

[10,20,30][1:]

[20,30]

[:end]從頭開始知道 end-1

[10,20,30][:2]

[10,20]

[start:end]從 start 到 end-1

[10,20,30,40][1:3]

[20,30]

[start:end:step] 從 start 提

取到 end-1,步長是 step

[10,20,30,40,50,60,70][1:6:

2]

[20, 40, 60]

其他操作(三個量為負數)的情況:

示例

說明

結果

[10,20,30,40,50,60,70][-3:]

倒數三個

[50,60,70]

10,20,30,40,50,60,70][-5:-3]

倒數第五個到倒數第三個(包頭不包尾)

[30,40]

[10,20,30,40,50,60,70][::-1]

步長為負,從右到左反向提取

[70, 60, 50, 40, 30, 20, 10]

切片操作時,起始偏移量和終止偏移量不在[0,字串長度-1]這個範圍,也不會報錯。起始

偏移量小於 0 則會當做 0,終止偏移量大於“長度-1”會被當成”長度-1”。例如:

>>> [10,20,30,40][1:30]

[20, 30, 40]

我們發現正常輸出了結果,沒有報錯。

列表的遍歷

for obj     in    listObj:

print(obj)

複製列表所有的元素到新列表物件

如下程式碼實現列表元素的複製了嗎? list1 = [30,40,50] list2 = list1

只是將 list2 也指向了列表物件,也就是說 list2 和 list2 持有地址值是相同的,列表物件本身的元素並沒有複製。

我們可以通過如下簡單方式,實現列表元素內容的複製: list1 = [30,40,50] list2 = [] + list1

注:我們後面也會學習 copy 模組,使用淺複製或深複製實現我們的複製操作。

列表排序修改原列表,不建新列表的排序

>>> a = [20,10,30,40]

>>> id(a)

46017416

>>> a.sort()                 #預設是升序排列

>>> a

[10, 20, 30, 40]

>>> a = [10,20,30,40]

>>> a.sort(reverse=True)         #降序排列

>>> a

[40, 30, 20, 10]

>>> import random

>>> random.shuffle(a)         #打亂順序

>>> a

[20, 40, 30, 10]

建新列表的排序

我們也可以通過內建函式 sorted()進行排序,這個方法返回新列表,不對原列表做修改。

>>> a = [20,10,30,40]

>>> id(a)

46016008

>>> a = sorted(a)                   #預設升序

>>> a

[10, 20, 30, 40]

>>> id(a)

45907848

>>> a = [20,10,30,40]

>>> id(a)

45840584

>>> b = sorted(a)

>>> b

[10, 20, 30, 40]

>>> id(a)

45840584 >>> id(b) 46016072

>>> c = sorted(a,reverse=True)          #降序

>>> c

[40, 30, 20, 10]

通過上面操作,我們可以看出,生成的列表物件 b 和 c 都是完全新的列表物件。 reversed()返回迭代器

內建函式 reversed()也支援進行逆序排列,與列表物件 reverse()方法不同的是,內建函式 reversed()不對原列表做任何修改,只是返回一個逆序排列的迭代器物件。

>>> a = [20,10,30,40]

>>> c = reversed(a)

>>> c

<list_reverseiterator object at 0x0000000002BCCEB8>

>>> list(c)

[40, 30, 10, 20]

>>> list(c)

[]

我們列印輸出 c 發現提示是:list_reverseiterator。也就是一個迭代物件。同時,我們使用 list(c)進行輸出,發現只能使用一次。第一次輸出了元素,第二次為空。那是因為迭代物件在第一次時已經遍歷結束了,第二次不能再使用。注:關於迭代物件的使用,後續章節會進行詳細講解。

列表相關的其他內建函式彙總

max 和 min

用於返回列表中最大和最小值。

[40, 30, 20, 10]

>>> a = [3,10,20,15,9]

>>> max(a)

20

>>> min(a)

3

sum

對數值型列表的所有元素進行求和操作,對非數值型列表運算則會報錯。

>>> a = [3,10,20,15,9]

>>> sum(a)

57

多維列表

二維列表

一維列表可以幫助我們儲存一維、線性的資料。

二維列表可以幫助我們儲存二維、表格的資料。例如下表的資料:

姓名

年齡

薪資

城市

高小一

18

30000

北京

高小二

19

20000

上海

高小五

20

10000

深圳

原始碼:

a = [

["高小一",18,30000,"北京"],

["高小二",19,20000,"上海"],

["高小一",20,10000,"深圳"],

]

記憶體結構圖:

 

>>> print(a[1][0],a[1][1],a[1][2])

高小二 19 20000

巢狀迴圈列印二維列表所有的資料(mypy_08.py)(由於沒有學迴圈,照著敲一遍即可):

a = [

["高小一",18,30000,"北京"],

["高小二",19,20000,"上海"],

["高小一",20,10000,"深圳"],

]

for m in range(3):

for n in range(4):

print(a[m][n],end="\t")

print() #列印完一行,換行

執行結果:高小一                  18    30000    北京高小二  19   20000    上海高小一 20   10000    深圳

元組 tuple

列表屬於可變序列,可以任意修改列表中的元素。元組屬於不可變序列,不能修改元組中的元素。因此,元組沒有增加元素、修改元素、刪除元素相關的方法。

因此,我們只需要學習元組的建立和刪除,元組中元素的訪問和計數即可。元組支援如下操作:

  1. 索引訪問
  2. 切片操作
  3. 連線操作
  4. 成員關係操作
  5. 比較運算操作
  6. 計數:元組長度 len()、最大值 max()、最小值 min()、求和 sum()等。

元組的建立

  1. 通過()建立元組。小括號可以省略。

a = (10,20,30)     或者       a = 10,20,30

如果元組只有一個元素,則必須後面加逗號。這是因為直譯器會把(1)解釋為整數 1,(1,) 解釋為元組。

>>> a = (1)

>>> type(a)

<class 'int'>

          >>> a = (1,)         #或者    a = 1,

>>> type(a)

<class 'tuple'>

  1. 通過 tuple()建立元組 tuple(可迭代的物件) 例如:

b = tuple()    #建立一個空元組物件 b = tuple("abc") b = tuple(range(3)) b = tuple([2,3,4])

總結: tuple()可以接收列表、字串、其他序列型別、迭代器等生成元組。

list()可以接收元組、字串、其他序列型別、迭代器等生成列表。

元組的元素訪問和計數

  1. 元組的元素不能修改

>>> a = (20,10,30,9,8)

>>> a[3]=33

Traceback (most recent call last):

File "<pyshell#313>", line 1, in <module> a[3]=33

TypeError: 'tuple' object does not support item assignment

  1. 元組的元素訪問和列表一樣,只不過返回的仍然是元組物件。

>>> a = (20,10,30,9,8)

>>> a[1]

10

>>> a[1:3]

(10, 30)

>>> a[:4]

(20, 10, 30, 9)

  1. 列表關於排序的方法 list.sorted()是修改原列表物件,元組沒有該方法。如果要對元組排序,只能使用內建函式 sorted(tupleObj),並生成新的列表物件。

>>> a = (20,10,30,9,8)

>>> sorted(a)

[8, 9, 10, 20, 30]

zip

zip(列表 1,列表 2,...)將多個列表對應位置的元素組合成為元組,並返回這個 zip 物件。

>>> a = [10,20,30]

>>> b = [40,50,60]

>>> c = [70,80,90]

>>> d = zip(a,b,c)

>>> list(d)

[(10, 40, 70), (20, 50, 80), (30, 60, 90)]

生成器推導式建立元組

從形式上看,生成器推導式與列表推導式類似,只是生成器推導式使用小括號。列表推

導式直接生成列表物件,生成器推導式生成的不是列表也不是元組,而是一個生成器物件。

我們可以通過生成器物件,轉化成列表或者元組。也可以使用生成器物件的__next__() 方法進行遍歷,或者直接作為迭代器物件來使用。不管什麼方式使用,元素訪問結束後,如果需要重新訪問其中的元素,必須重新建立該生成器物件。

【操作】生成器的使用測試

>>> s = (x*2 for x in range(5))

>>> s

<generator object <genexpr> at 0x0000000002BDEB48>

>>> tuple(s)

(0, 2, 4, 6, 8)

>>> list(s)               #只能訪問一次元素。第二次就為空了。需要再生成一次

[]

>>> s

<generator object <genexpr> at 0x0000000002BDEB48>

>>> tuple(s)

()

>>> s = (x*2 for x in range(5))

>>> s.__next__()

0

>>> s.__next__()

2

>>> s.__next__() 4

元組總結

  1. 元組的核心特點是:不可變序列。
  2. 元組的訪問和處理速度比列表快。
  3. 與整數和字串一樣,元組可以作為字典的鍵,列表則永遠不能作為字典的鍵使用。

字典介紹

字典是“鍵值對”的無序可變序列,字典中的每個元素都是一個“鍵值對”,包含:“鍵

物件”和“值物件”。可以通過“鍵物件”實現快速獲取、刪除、更新對應的“值物件”。

列表中我們通過“下標數字”找到對應的物件。字典中通過“鍵物件”找到對應的“值

物件”。“鍵”是任意的不可變資料,比如:整數、浮點數、字串、元組。但是:列表、字典、集合這些可變物件,不能作為“鍵”。並且“鍵”不可重複。 “值”可以是任意的資料,並且可重複。

一個典型的字典的定義方式: a = {'name':'gaoqi','age':18,'job':'programmer'}

字典的建立

  1. 我們可以通過{}、dict()來建立字典物件。

>>> a = {'name':'gaoqi','age':18,'job':'programmer'}

>>> b = dict(name='gaoqi',age=18,job='programmer')

>>> a = dict([("name","gaoqi"),("age",18)])

          >>> c = {}     #空的字典物件

          >>> d = dict()     #空的字典物件

  1. 通過 zip()建立字典物件

>>> k = ['name','age','job']

>>> v = ['gaoqi',18,'techer']

>>> d = dict(zip(k,v))

>>> d

{'name': 'gaoqi', 'age': 18, 'job': 'techer'}

  1. 通過 fromkeys 建立值為空的字典

>>> a = dict.fromkeys(['name','age','job'])

>>> a

{'name': None, 'age': None, 'job': None}

字典元素的訪問

為了測試各種訪問方法,我們這裡設定一個字典物件:

a = {'name':'gaoqi','age':18,'job':'programmer'}

  1. 通過  [鍵] 獲得“值”。若鍵不存在,則丟擲異常。

>>> a = {'name':'gaoqi','age':18,'job':'programmer'}

>>> a['name']

'gaoqi'

>>> a['age']

18

>>> a['sex']

Traceback (most recent call last):

File "<pyshell#374>", line 1, in <module> a['sex']

KeyError: 'sex'

  1. 通過 get()方法獲得“值”。推薦使用。優點是:指定鍵不存在,返回 None;也可以設定指定鍵不存在時預設返回的物件。推薦使用 get()獲取“值物件”。

>>> a.get('name')

'gaoqi'

>>> a.get('sex')

>>> a.get('sex','一個男人')

'一個男人'

  1. 列出所有的鍵值對

>>> a.items()

dict_items([('name', 'gaoqi'), ('age', 18), ('job', 'programmer')])

  1. 列出所有的鍵,列出所有的值

>>> a.keys()

dict_keys(['name', 'age', 'job'])

>>> a.values()

dict_values(['gaoqi', 18, 'programmer'])

  1. len() 鍵值對的個數
  2. 檢測一個“鍵”是否在字典中

>>> a = {"name":"gaoqi","age":18}

>>> "name" in a

True

字典元素新增、修改、刪除

  1. 給字典新增“鍵值對”。如果“鍵”已經存在,則覆蓋舊的鍵值對;如果“鍵”不存在,則新增“鍵值對”。

>>>a = {'name':'gaoqi','age':18,'job':'programmer'}

>>> a['address']='西三旗 1 號院'

>>> a['age']=16

>>> a

{'name': 'gaoqi', 'age': 16, 'job': 'programmer', 'address': '西三旗 1 號院'}

  1. 使用 update()將新字典中所有鍵值對全部新增到舊字典物件上。如果 key 有重複,則直接覆蓋。

>>> a = {'name':'gaoqi','age':18,'job':'programmer'}

>>> b = {'name':'gaoxixi','money':1000,'sex':'男的'}

>>> a.update(b)

>>> a

{'name': 'gaoxixi', 'age': 18, 'job': 'programmer', 'money': 1000, 'sex': '男的'}

  1. 字典中元素的刪除,可以使用 del()方法;或者 clear()刪除所有鍵值對;pop()刪除指定

鍵值對,並返回對應的“值物件”;

>>> a = {'name':'gaoqi','age':18,'job':'programmer'}

>>> del(a['name'])

>>> a

{'age': 18, 'job': 'programmer'}

>>> b = a.pop('age')

>>> b

18

  1. popitem() :隨機刪除和返回該鍵值對。字典是“無序可變序列”,因此沒有第一個元素、最後一個元素的概念;popitem 彈出隨機的項,因為字典並沒有"最後的元素"或者其他有關順序的概念。若想一個接一個地移除並處理項,這個方法就非常有效(因為不用首先獲取鍵的列表)。

>>> a = {'name':'gaoqi','age':18,'job':'programmer'}

>>> a.popitem()

('job', 'programmer')

>>> a

{'name': 'gaoqi', 'age': 18}

>>> a.popitem()

('age', 18)

>>> a

{'name': 'gaoqi'}

序列解包

序列解包可以用於元組、列表、字典。序列解包可以讓我們方便的對多個變數賦值。

>>> x,y,z=(20,30,10)

>>> x

20

>>> y

30

>>> z

10

>>> (a,b,c)=(9,8,10)

>>> a

9

>>> [a,b,c]=[10,20,30]

>>> a

10

>>> b

20

序列解包用於字典時,預設是對“鍵”進行操作; 如果需要對鍵值對操作,則需要使用 items();如果需要對“值”進行操作,則需要使用 values();

>>> s = {'name':'gaoqi','age':18,'job':'teacher'}

           >>> name,age,job=s                     #預設對鍵進行操作

>>> name

'name'

           >>> name,age,job=s.items()         #對鍵值對進行操作

>>> name

('name', 'gaoqi')

           >>> name,age,job=s.values()               #對值進行操作

>>> name

'gaoqi'

表格資料使用字典和列表儲存,並實現訪問

姓名

年齡

薪資

城市

高小一

18

30000

北京

高小二

19

20000

上海

高小五

20

10000

深圳

原始碼(mypy_09.py):

r1 = {"name":"高小一","age":18,"salary":30000,"city":"北京"} r2 = {"name":"高小二","age":19,"salary":20000,"city":"上海"} r3 = {"name":"高小五","age":20,"salary":10000,"city":"深圳"} tb = [r1,r2,r3]

#獲得第二行的人的薪資

print(tb[1].get("salary"))

#列印表中所有的的薪資

for i in range(len(tb)):        # i -->0,1,2

print(tb[i].get("salary"))

#列印表的所有資料

for i in range(len(tb)):

print(tb[i].get("name"),tb[i].get("age"),tb[i].get("salary"),tb[i].get("city"))

字典核心底層原理(重要)

字典物件的核心是雜湊表。雜湊表是一個稀疏陣列(總是有空白元素的陣列),陣列的每個單元叫做 bucket。每個 bucket 有兩部分:一個是鍵物件的引用,一個是值物件的引用。

由於,所有 bucket 結構和大小一致,我們可以通過偏移量來讀取指定 bucket。

 

將一個鍵值對放進字典的底層過程

>>> a = {}

>>> 

a["name"]="gaoqi"

假設字典 a 物件建立完後,陣列長度為 8:

 

我們要把”name”=”gaoqi”這個鍵值對放到字典物件 a 中,首先第一步需要計算鍵”name”的雜湊值。Python 中可以通過 hash()來計算。

>>> bin(hash("name"))

'-0b1010111101001110110101100100101'

由於陣列長度為 8,我們可以拿計算出的雜湊值的最右邊 3 位數字作為偏移量,即 “101”,十進位制是數字 5。我們檢視偏移量 5,對應的 bucket 是否為空。如果為空,則將鍵值對放進去。如果不為空,則依次取右邊 3 位作為偏移量,即“100”,十進位制是數字 4。再檢視偏移量為 4 的 bucket 是否為空。直到找到為空的 bucket 將鍵值對放進去。流程圖如下:

 

擴容

python 會根據雜湊表的擁擠程度擴容。“擴容”指的是:創造更大的陣列,將原有內容拷貝到新陣列中。

接近 2/3 時,陣列就會擴容。

根據鍵查詢“鍵值對”的底層過程

我們明白了,一個鍵值對是如何儲存到陣列中的,根據鍵物件取到值物件,理解起來就簡單了。

>>> a.get("name")

'gaoqi'

當我們呼叫 a.get(“name”),就是根據鍵“name”查詢到“鍵值對”,從而找到值物件“gaoqi”。

第一步,我們仍然要計算“name”物件的雜湊值:

>>> bin(hash("name"))

'-0b1010111101001110110101100100101'

和儲存的底層流程演算法一致,也是依次取雜湊值的不同位置的數字。 假設陣列長度為

8,我們可以拿計算出的雜湊值的最右邊 3 位數字作為偏移量,即“101”,十進位制是數字

5。我們檢視偏移量 5,對應的 bucket 是否為空。如果為空,則返回 None。如果不為空,則將這個 bucket 的鍵物件計算對應雜湊值,和我們的雜湊值進行比較,如果相等。則將對應“值物件”返回。如果不相等,則再依次取其他幾位數字,重新計算偏移量。依次取完後,仍然沒有找到。則返回 None。流程圖如下:

 

用法總結:

  1. 鍵必須可雜湊

(1)    數字、字串、元組,都是可雜湊的。

(2)    自定義物件需要支援下面三點:

1支援 hash()函式

2支援通過__eq__()方法檢測相等性。

3若 a==b 為真,則 hash(a)==hash(b)也為真。

  1. 字典在記憶體中開銷巨大,典型的空間換時間。
  2. 鍵查詢速度很快
  3. 往字典裡面新增新建可能導致擴容,導致雜湊表中鍵的次序變化。因此,不要在遍歷字典的同時進行字典的修改。

集合

集合是無序可變,元素不能重複。實際上,集合底層是字典實現,集合的所有元素都是字典中的“鍵物件”,因此是不能重複的且唯一的。

集合建立和刪除

  1. 使用{}建立集合物件,並使用 add()方法新增元素

>>> a = {3,5,7}

>>> a

{3, 5, 7}

>>> a.add(9)

>>> a

{9, 3, 5, 7}

  1. 使用 set(),將列表、元組等可迭代物件轉成集合。如果原來資料存在重複資料,則只保留一個。

>>> a = ['a','b','c','b']

>>> b = set(a)

>>> b

{'b', 'a', 'c'}

  1. remove()刪除指定元素;clear()清空整個集合

>>> a = {10,20,30,40,50}

>>> a.remove(20)

>>> a

{10, 50, 30}

集合相關操作

像數學中概念一樣,Python 對集合也提供了並集、交集、差集等運算。我們給出示例:

>>> a = {1,3,'sxt'}

>>> b = {'he','it','sxt'}

          >>> a|b                            #並集

{1, 3, 'sxt', 'he', 'it'}

 

>>> a&b

{'sxt'}

#交集

>>> a-b

{1, 3}

#差集

>>> a.union(b)

{1, 3, 'sxt', 'he', 'it'}

#並集

>>> a.intersection(b)

{'sxt'}

#交集

>>> a.difference(b)

{1, 3}

#差集

章節實操作業

  1. 畫出程式碼 a = [100,200,300]的記憶體儲存示意圖。
  2. 使用 range 生成序列:30,40,50,60,70,80
  3. 推導式生成列表: a = [x*2 for x in range(100) if x%9==0],手寫出結果。
  4. 使用二維列表儲存表格資訊,並畫出簡單的記憶體儲存示意圖:

姓名

年齡

薪資

城市

高小一

18

30000

北京

高小二

19

20000

上海

高小五

20

10000

深圳

  1. 元組和列表有哪些共同點?有哪些不同點?
  2. 建立一個字典物件,包含如下資訊:

支出金額:300.15,支出日期:2018.10.18,支出人:高小七

  1. 使用字典儲存行資料,最後將整個表使用列表儲存起來。

姓名

年齡

薪資

城市

高小一

18

30000

北京

高小二

19

20000

上海

高小五

20

10000

深圳

  1. 用文字和自己畫的示意圖描述:字典儲存一個鍵值對的底層過程。
  2. 集合和字典有什麼關係?

第4章 控制語句

我們在前面學習的過程中,都是很短的示例程式碼,沒有進行復雜的操作。現在,我們將開始學習流程控制語句。

前面學習的變數、資料型別(整數、浮點數、布林)、序列(字串、列表、元組、字典、集合),可以看做是資料的組織方式。資料可以看做是“磚塊”!流程控制語句是程式碼的組織方式,可以看做是“混凝土”。

一個完整的程式,離不開“磚塊”,也離不開“混凝土”。他們的組合,才能讓我們建立從小到“一個方法”,大到“作業系統”,這樣各種各樣的“軟體”。

PyCharm 開發環境的使用

開始學習控制語句,就會有大量的練習,我們開始學習更加強大的 IDE。目前,比較流行的

IDE 是 PyCharm。當然,還有其他 IDE 可供我們使用:

  1. IDLE
  2. PyCharm
  3. wingIDE
  4. Eclipse
  5. IPython

PyCharm 下載和安裝

下載地址:https://www.jetbrains.com/pycharm/download/#section=windows

下載對應的版本:

 

和安裝普通軟體一致,點選下一步即可。只有幾個畫面需要單獨關注。根據 win 系統是 64 還是 32 位,選擇不同的型別。

 

不匯入配置:

 

將右側滾動條拖到最下面,然後選擇“accept”

 

不傳送訊息,選擇“Don’t send”:

 

啟用和選擇不同 UI 風格

啟用的方式:

(1)    購買正版

(2)    試用 30 天

(3)    網上尋找啟用碼

沒有啟用碼,選擇試用:

 

若有啟用碼,則輸入啟用碼:

 

選擇不同的 UI 風格:

 

根據自己喜好,選擇 Dracula 風格或者 IntelliJ 風格。然後點選“Skip Remaining and Set

Defaults”

建立專案和初始配置

1. 選擇:“Create New Project”

 

選擇路徑(儘量不要包含中文),檔名:mypro01 就是我們的專案名稱,可以修改。

 

其他:

  1. Project Interpreter 部分是選擇新建專案所依賴的 python 庫,第一個選項會在專案中建立一個 venv(virtualenv)目錄,這裡存放一個虛擬的 python 環境。這裡所有的類庫依賴都可以直接脫離系統安裝的 python 獨立執行。

3.Existing Interpreter 關聯已經存在的 python 直譯器,如果不想在專案中出現 venv 這個虛擬直譯器就可以選擇本地安裝的 python 環境。

那麼到底這兩個該怎麼去選擇呢,這裡建議選擇 New Environment 可以在 Base Interpreter 選擇系統中安裝的 Python 直譯器,這樣做的好處如下:

  1. python 專案可以獨立部署
  2. 防止一臺伺服器部署多個專案之間存在類庫的版本依賴問題發生
  3. 也可以充分發揮專案的靈活性
  4. 開啟專案後,右鍵單擊專案,建立 Python 檔案“mypy01”

 

  1. 執行 py 檔案,使用右鍵單擊編輯區,選擇“Run ‘mypy01’”即可。

 

·字型大小設定

File→Setting→Editor→Font 把字型調大一些

選擇結構

選擇結構通過判斷條件是否成立,來決定執行哪個分支。選擇結構有多種形式,分為:單分支、雙分支、多分支。流程圖如下:

                  單分支結構                                             雙分支結構

 

多分支結構

 

單分支選擇結構

if 語句單分支結構的語法形式如下: if 條件表示式:

語句/語句塊

其中:

1.條件表示式:可以是邏輯表示式、關係表示式、算術表示式等等。

2.語句/語句塊:可以是一條語句,也可以是多條語句。多條語句,縮排必須對齊一致。

【操作】輸入一個數字,小於 10,則列印這個數字(if_test01.py)

num = input("輸入一個數字:") if int(num)<10:

print(num)

條件表示式詳解

在選擇和迴圈結構中,條件表示式的值為 False 的情況如下:

False、0、0.0、空值 None、空序列物件(空列表、空元祖、空集合、空字典、空字串)、空 range 物件、空迭代物件。

其他情況,均為 True。這麼看來,Python 所有的合法表示式都可以看做條件表示式,甚至包括函式呼叫的表示式。

【操作】測試各種條件表示式

if 3:      #整數作為條件表示式

print("ok")

a = []      #列表作為條件表示式,由於為空列表,是 False if a:

print("空列表,False")

s = "False"     #非空字串,是 True if s:

print("非空字串,是 True")

c = 9 if 3<c<20:

print("3<c<20") if 3<c       and c<20: print("3<c       and c<20")

if True:              #布林值

print("True")

執行結果如下:

ok

非空字串,是 True

3<c<20

3<c    and c<20

True >>>

·條件表示式中,不能有賦值操作符“=”

在 Python 中,條件表示式不能出現賦值操作符“=”,避免了其他語言中經常誤將關係運

算符“==”寫作賦值運算子“=”帶來的困擾。如下程式碼將會報語法錯誤: if 3<c and (c=20):

print("賦值符不能出現在條件表示式中") 雙分支選擇結構

雙分支結構的語法格式如下: if     條件表示式 :

語句 1/語句塊 1 else:

語句 2/語句塊 2

【操作】輸入一個數字,小於 10,則列印該數字;大於 10,則列印“數字太大”

num = input("輸入一個數字:") if int(num)<10:

print(num) else:

print("數字太大")

三元條件運算子

Python 提供了三元運算子,用來在某些簡單雙分支賦值情況。三元條件運算子語法格式如下:

if

(條件表示式)

else

條件為真時的值條件為假時的值

上一個案例程式碼,可以用三元條件運算子實現:

num = input("請輸入一個數字")

print( num if int(num)<10 else "數字太大")

可以看到,這種寫法更加簡潔,易讀。

多分支選擇結構

多分支選擇結構的語法格式如下: if 條件表示式 1 :

語句 1/語句塊 1 elif 條件表示式 2:

語句 2/語句塊 2

.

. .

elif 條件表示式 n :

語句 n/語句塊 n

[else:

語句 n+1/語句塊 n+1

]

【注】計算機行業,描述語法格式時,使用中括號[]通常表示可選,非必選。

 

多分支結構,幾個分支之間是有邏輯關係的,不能隨意顛倒順序。

【操作】輸入一個學生的成績,將其轉化成簡單描述:不及格(小於 60)、及格(60-79)、良好(80-89)、優秀(90-100)。

方法 1(使用完整的條件表達)

score = int(input("請輸入分數")) grade = '' if(score<60):

grade = "不及格" if(60<=score<80):

grade = "及格" if(80<=score<90):

grade = "良好" if(90<=score<=100): grade = "優秀"

print("分數是{0},等級是{1}".format(score,grade))

每個分支都使用了獨立的、完整的判斷,順序可以隨意挪動,而不影響程式執行。

方法 2(利用多分支結構)

score = int(input("請輸入分數")) grade = '' if score<60 :

grade = "不及格" elif   score<80 : grade = "及格" elif       score<90 : grade = "良好" elif       score<=100:

grade = "優秀"

print("分數是{0},等級是{1}".format(score,grade))

多分支結構,幾個分支之間是有邏輯關係的,不能隨意顛倒順序。

【操作】已知點的座標(x,y),判斷其所在的象限

x = int(input("請輸入 x 座標")) y = int(input("請輸入 y 座標"))

if(x==0 and y==0):print("原點")

elif(x==0):print("y 軸") elif(y==0):print("x 軸") elif(x>0 and y>0):print("第一象限") elif(x<0 and y>0):print("第二象限") elif(x<0 and y<0):print("第三象限") else:

print("第四象限")

選擇結構巢狀

選擇結構可以巢狀,使用時一定要注意控制好不同級別程式碼塊的縮排量,因為縮排量決定了程式碼的從屬關係。語法格式如下:

if 表示式 1:

語句塊 1

if 表示式 2:語句塊 2 else:

語句塊 3 else:

if 表示式 4:語句塊 4

【操作】輸入一個分數。分數在 0-100 之間。90 以上是 A,80 以上是 B,70 以上是 C,60 以上是 D。60 以下是 E。

score = int(input("請輸入一個在0-100之間的數字:")) grade = "" if score>100 or score<0:

score = int(input("輸入錯誤!請重新輸入一個在0-100之間的數字:")) else:

if score>=90: grade = "A" elif score>=80: grade = 'B' elif score>=70: grade = 'C' elif score>=60:

grade = 'D' else:

grade = 'E'

print("分數為{0},等級為{1}".format(score,grade))

#或者也可以用下面程式碼更少的方法。不過,需要大家思考為什麼這麼寫了

score = int(input("請輸入一個在0-100之間的數字:")) degree = "ABCDE" num = 0 if score>100 or score<0:

score = int(input("輸入錯誤!請重新輸入一個在0-100之間的數字:")) else:

num = score//10 if num<6:num=5 print("分數是{0},等級是{1}".format(score,degree[9-num]))

迴圈結構

迴圈結構用來重複執行一條或多條語句。表達這樣的邏輯:如果符合條件,則反覆執行迴圈體裡的語句。在每次執行完後都會判斷一次條件是否為 True,如果為 True 則重複執行迴圈體裡的語句。圖示如下:

 

迴圈體裡面的語句至少應該包含改變條件表示式的語句,以使迴圈趨於結束;否則,就會變成一個死迴圈。

while 迴圈

while 迴圈的語法格式如下:

while      條件表示式:迴圈體語句

我們通過一些簡單的練習,來慢慢熟悉 while 迴圈。

【操作】利用 while 迴圈列印從 0-10 的數字。

num = 0 while num<=10: print(num)

num += 1

【操作】利用 while 迴圈,計算 1-100 之間數字的累加和;計算 1-100 之間偶數的累加和,計算 1-100 之間奇數的累加和。

num = 0

sum_all = 0

#1-100 所有數的累加和

sum_even = 0      #1-100 偶數的累加和 sum_odd = 0  #1-100 奇數的累加和 while num<=100:

sum_all += num

if num%2==0:sum_even += num else:sum_odd += num

            num += 1            #迭代,改變條件表示式,使迴圈趨於結束

print("1-100 所有數的累加和",sum_all) print("1-100 偶數的累加和",sum_even) print("1-100 奇數的累加和",sum_odd)

for 迴圈和可迭代物件遍歷

for 迴圈通常用於可迭代物件的遍歷。for 迴圈的語法格式如下: for  變數       in 可迭代物件:迴圈體語句

【操作】遍歷一個元組或列表

for    x    in    (20,30,40):

print(x*3)

可迭代物件

Python 包含以下幾種可迭代物件:

  1. 序列。包含:字串、列表、元組
  2. 字典
  3. 迭代器物件(iterator)
  4. 生成器函式(generator)
  5. 檔案物件

我們已經在前面學習了序列、字典等知識,迭代器物件和生成器函式將在後面進行詳解。接下來,我們通過迴圈來遍歷這幾種型別的資料:

【操作】遍歷字串中的字元

for x    in    "sxt001":

print(x)

【操作】遍歷字典

d = {'name':'gaoqi','age':18,'address':'西三旗 001 號樓'}

for x    in    d:   #遍歷字典所有的 key

print(x)

for x      in   d.keys():#遍歷字典所有的 key

print(x)

for x      in    d.values():#遍歷字典所有的 value

print(x)

for x      in   d.items():#遍歷字典所有的"鍵值對"

print(x)

range 物件

range 物件是一個迭代器物件,用來產生指定範圍的數字序列。格式為:

                                                                  range(start, end     [,step])

生成的數值序列從 start 開始到 end 結束(不包含 end)。若沒有填寫 start,則預設從 0

開始。step 是可選的步長,預設為 1。如下是幾種典型示例: for i in range(10)     產生序列:0 1 2 3 4 5 6 7 8 9 for i in range(3,10)    產生序列:3 4 5 6 7 8 9 for i in range(3,10,2) 產生序列:3 5     7     9

【操作】利用 for 迴圈,計算 1-100 之間數字的累加和;計算 1-100 之間偶數的累加和,計算 1-100 之間奇數的累加和。

sum_all = 0       #1-100 所有數的累加和 sum_even = 0       #1-100 偶數的累加和 sum_odd = 0 #1-100 奇數的累加和 for num in range(101): sum_all += num

if num%2==0:sum_even += num else:sum_odd += num print("1-100 累加總和{0},奇數和{1},偶數和{2}".format(sum_all,sum_odd,sum_even))

巢狀迴圈和綜合練習

一個迴圈體內可以嵌入另一個迴圈,一般稱為“巢狀迴圈”,或者“多重迴圈”。

【操作】列印如下圖案

0 0 0 0 0 1 1 1 1                                                                                                          1

2 2 2 2                                                                                                                          2

3 3 3 3                                                                                                                          3

4 4 4 4                                                                                                                          4

 

for x in range(5):

for y in range(5): print(x,end="\t") print() #僅用於換行

【操作】利用巢狀迴圈列印九九乘法表

for m in range(1,10):

for n in range(1,m+1):

print("{0}*{1}={2}".format(m,n,(m*n)),end="\t")

print()

執行結果:

1*1=1

          2*1=2    2*2=4

          3*1=3    3*2=6    3*3=9

          4*1=4    4*2=8     4*3=12 4*4=16

          5*1=5      5*2=10 5*3=15 5*4=20 5*5=25

          6*1=6       6*2=12 6*3=18 6*4=24 6*5=30 6*6=36

          7*1=7       7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49

          8*1=8        8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64

9*1=9    9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 【操作】用列表和字典儲存下表資訊,並列印出表中工資高於 15000 的資料

姓名

年齡

薪資

城市

高小一

18

30000

北京

高小二

19

20000

上海

高小五

20

10000

深圳

r1= dict(name="高小一",age=18,salary=30000,city="北京") r2= dict(name="高小二",age=19,salary=20000,city="上海") r3= dict(name="高小三",age=20,salary=10000,city="深圳") tb = [r1,r2,r3]

for x in tb: if x.get("salary")>15000:

print(x)

break 語句

break 語句可用於 while 和 for 迴圈,用來結束整個迴圈。當有巢狀迴圈時,break 語句只能跳出最近一層的迴圈。

【操作】使用 break 語句結束迴圈

while True:

a = input("請輸入一個字元(輸入 Q 或 q 結束)") if a.upper()=='Q':

print("迴圈結束,退出")

break else:

print(a)

continue 語句

continue 語句用於結束本次迴圈,繼續下一次。多個迴圈巢狀時,continue 也是應用於最近的一層迴圈。

【操作】要求輸入員工的薪資,若薪資小於 0 則重新輸入。最後列印出錄入員工的數量和薪資明細,以及平均薪資

empNum = 0 salarySum= 0 salarys = [] while True:

s = input("請輸入員工的薪資(按 Q 或 q 結束)")

if s.upper()=='Q':

print("錄入完成,退出")

break

if float(s)<0: continue

empNum +=1

salarys.append(float(s)) salarySum += float(s)

print("員工數{0}".format(empNum)) print("錄入薪資:",salarys)

print("平均薪資{0}".format(salarySum/empNum))

執行結果:請輸入員工的薪資(按 Q 或 q 結束)2000 請輸入員工的薪資(按 Q 或 q 結束)3000 請輸入員工的薪資(按 Q 或 q 結束)4000 請輸入員工的薪資(按 Q 或 q 結束)5000 請輸入員工的薪資(按 Q 或 q 結束)Q 錄入完成,退出

員工數 4

錄入薪資: [2000.0, 3000.0, 4000.0, 5000.0] 平均薪資 3500.0 else 語句

while、for 迴圈可以附帶一個 else 語句(可選)。如果 for、while 語句沒有被 break 語句結束,則會執行 else 子句,否則不執行。語法格式如下:

while      條件表示式:迴圈體 else:

語句塊

或者:

for  變數       in    可迭代物件:迴圈體 else:

語句塊

【操作】員工一共 4 人。錄入這 4 位員工的薪資。全部錄入後,列印提示“您已經全部錄入 4 名員工的薪資”。最後,列印輸出錄入的薪資和平均薪資

salarySum= 0 salarys = [] for i in range(4):

s = input("請輸入一共 4 名員工的薪資(按 Q 或 q 中途結束)")

if s.upper()=='Q':

print("錄入完成,退出")

break

if float(s)<0:

continue

salarys.append(float(s)) salarySum += float(s)

else:

print("您已經全部錄入 4 名員工的薪資")

print("錄入薪資:",salarys)

print("平均薪資{0}".format(salarySum/4))

迴圈程式碼優化雖然計算機越來越快,空間也越來越大,我們仍然要在效能問題上“斤斤計較”。編寫迴圈時,遵守下面三個原則可以大大提高執行效率,避免不必要的低效計算:

  1. 儘量減少迴圈內部不必要的計算
  2. 巢狀迴圈中,儘量減少內層迴圈的計算,儘可能向外提。
  3. 區域性變數查詢較快,儘量使用區域性變數

#迴圈程式碼優化測試 import time

start = time.time() for i in range(1000): result = [] for m in range(10000):

result.append(i*1000+m*100)

end = time.time()

print("耗時:{0}".format((end-start)))

start2 = time.time() for i in range(1000):

result = [] c = i*1000 for m in range(10000):

result.append(c+m*100)

end2 = time.time()

print("耗時:{0}".format((end2-start2)))

其他優化手段

  1. 連線多個字串,使用 join()而不使用+
  2. 列表進行元素插入和刪除,儘量在列表尾部操作使用 zip()並行迭代我們可以通過 zip()函式對多個序列進行並行迭代,zip()函式在最短序列“用完”時就會停止。

【操作】測試 zip()並行迭代

names = ("高淇","高老二","高老三","高老四")

ages = (18,16,20,25) jobs = ("老師","程式設計師","公務員")

for name,age,job in zip(names,ages,jobs): print("{0}--{1}--{2}".format(name,age,job))

執行結果:

高淇--18--老師高老二--16--程式設計師高老三--20--公務員

推導式建立序列

推導式是從一個或者多個迭代器快速建立序列的一種方法。它可以將迴圈和條件判斷結合,從而避免冗長的程式碼。推導式是典型的 Python 風格,會使用它代表你已經超過 Python 初學者的水平。

列表推導式

列表推導式生成列表物件,語法如下:

[表示式 for item in 可迭代物件 ] 或者:{表示式 for item in 可迭代物件 if 條件判斷}

>>> [x for x in range(1,5)]

[1, 2, 3, 4]

>>> [x*2 for x in range(1,5)]

[2, 4, 6, 8]

>>> [x*2 for x in range(1,20) if x%5==0 ]

[10, 20, 30]

>>> [a     for    a    in "abcdefg"]

['a', 'b', 'c', 'd', 'e', 'f', 'g']

>>> cells = [(row,col) for         row in range(1,10) for col in range(1,10)]          #可以使用兩

個迴圈

>>> for cell in cells: print(cell)

字典推導式

字典的推導式生成字典物件,格式如下:

                {key_expression     :    value_expression      for    表示式     in   可迭代物件}

類似於列表推導式,字典推導也可以增加 if 條件判斷、多個 for 迴圈。

統計文字中字元出現的次數:

>>> my_text = ' i love you, i love sxt, i love gaoqi'

>>> char_count = {c:my_text.count(c)            for c in my_text}

>>> char_count

{' ': 9, 'i': 4, 'l': 3, 'o': 5, 'v': 3, 'e': 3, 'y': 1, 'u': 1, ',': 2, 's': 1, 'x': 1, 't': 1, 'g': 1, 'a': 1, 'q': 1}

集合推導式

集合推導式生成集合,和列表推導式的語法格式類似:

{表示式 for   item in 可迭代物件 } 或者:{表示式     for   item in 可迭代物件     if      條件判斷}

>>> {x for x in range(1,100) if x%9==0} {99, 36, 72, 9, 45, 81, 18, 54, 90, 27, 63} 生成器推導式(生成元組)

很多同學可能會問:“都有推導式,元組有沒有?”,能不能用小括號呢?

>>> (x for x in range(1,100) if x%9==0)

<generator object <genexpr> at 0x0000000002BD3048>

我們發現提示的是“一個生成器物件”。顯然,元組是沒有推導式的。一個生成器只能執行一次。第一次迭代可以得到資料,第二次迭代發現資料已經沒有了。

>>> gnt = (x for x in range(1,100) if x%9==0) >>> for x in gnt:

print(x,end=' ')

9 18 27 36 45 54 63 72 81 90 99 >>> for x in gnt:

print(x,end=' ')

>>> 

綜合練習

  1. 【操作】繪製多個同心圓

import turtle t = turtle.Pen() my_colors = ("red","green","yellow","black")

t.width(4)

t.speed(1)

for i in range(10): #0 1 2 3 4

t.penup()

t.goto(0,-i*10) #0,-100,-200,-300,-400

t.pendown()

t.color(my_colors[i%len(my_colors)])

t.circle(15+i*10) #100,200,300,400,,500

turtle.done() #程式執行完,視窗仍然在

執行效果:

 

  1. 【操作】繪製 18*18 棋盤

#畫棋盤

import turtle

width = 30 num = 18

x1 = [(-400,400),(-400+width*num,400)] y1 = [(-400,400),(-400,400-width*num)] t = turtle.Pen() t.speed(10)

#t.goto(x1[0][0],x1[0][1])

#t.goto(x1[1][0],x1[1][1])

for i in range(0,19):

t.penup()

t.goto(x1[0][0],x1[0][1]-30*i)

t.pendown()

t.goto(x1[1][0],x1[1][1]-30*i)

for i in range(0,19):

t.penup()

t.goto(y1[0][0]+30*i,y1[0][1])

t.pendown()

t.goto(y1[1][0]+30*i,y1[1][1])

t.hideturtle() #隱藏畫筆 turtle.done() #保證執行視窗不被自動關閉

執行結果:

 

實操作業

  1. 安裝 Pycharm 開發環境,並使用圖文描述整個過程。
  2. 輸入一個學生的成績,將其轉化成簡單描述:不及格(小於 60)、及格(60-79)、良好

(80-89)、優秀(90-100)

  1. 已知點的座標(x,y),判斷其所在的象限
  2. 輸入一個分數。分數在 0-100 之間。90 以上是 A,80 以上是 B,70 以上是 C,60 以上是 D。60 以下是 E
  3. 利用 while 迴圈,計算 1-100 之間數字的累加和;計算 1-100 之間偶數的累加和,計算

1-100 之間奇數的累加和

  1. 利用 for 迴圈,計算 1-100 之間數字的累加和;計算 1-100 之間偶數的累加和,計算

1-100 之間奇數的累加和

  1. 列印如下圖案

0       0     0     0     0

1       1     1     1     1

2       2     2     2     2

3       3     3     3     3

4       4     4     4     4

  1. 利用巢狀迴圈列印九九乘法表
  2. 用列表和字典儲存下表資訊,並列印出表中工資高於 15000 的資料

姓名

年齡

薪資

城市

高小一

18

30000

北京

高小二

19

20000

上海

高小五

20

10000

深圳

  1. 要求輸入員工的薪資,若薪資小於 0 則重新輸入。最後列印出錄入員工的數量和薪資明細,以及平均薪資
  2. 員工一共 4 人。錄入這 4 位員工的薪資。全部錄入後,列印提示“您已經全部錄入 4 名員工的薪資”。最後,列印輸出錄入的薪資和平均薪資
  3. 使用海龜繪圖,繪製同心圓:

 

  1. 使用海龜繪圖,繪製 18*18 的棋盤:

 

第5章 函式用法和底層分析

函式是可重用的程式程式碼塊。函式的作用,不僅可以實現程式碼的複用,更能實現程式碼的一致性。一致性指的是,只要修改函式的程式碼,則所有呼叫該函式的地方都能得到體現。

在編寫函式時,函式體中的程式碼寫法和我們前面講述的基本一致,只是對程式碼實現了封裝,並增加了函式呼叫、傳遞引數、返回計算結果等內容。

為了讓大家更容易理解,掌握的更深刻。我們也要深入記憶體底層進行分析。絕大多數語言記憶體底層都是高度相似的,這樣大家掌握了這些內容也便於以後學習其他語言。

函式簡介

函式的基本概念

  1. 一個程式由一個個任務組成;函式就是代表一個任務或者一個功能。
  2. 函式是程式碼複用的通用機制。

Python 函式的分類

Python 中函式分為如下幾類:

  1. 內建函式

我們前面使用的 str()、list()、len()等這些都是內建函式,我們可以拿來直接使用。

  1. 標準庫函式

我們可以通過 import 語句匯入庫,然後使用其中定義的函式

  1. 第三方庫函式

Python 社群也提供了很多高質量的庫。下載安裝這些庫後,也是通過 import 語句匯入,然後可以使用這些第三方庫的函式

  1. 使用者自定義函式

使用者自己定義的函式,顯然也是開發中適應使用者自身需求定義的函式。今天我們學習的就是如何自定義函式。

函式的定義和呼叫

核心要點

Python 中,定義函式的語法如下: def 函式名 ([引數列表]) :

'''文件字串''' 函式體/若干語句

要點:

  1. 我們使用 def 來定義函式,然後就是一個空格和函式名稱;

(1)    Python 執行 def 時,會建立一個函式物件,並繫結到函式名變數上。

  1. 引數列表

(1)    圓括號內是形式引數列表,有多個引數則使用逗號隔開

(2)    形式引數不需要宣告型別,也不需要指定函式返回值型別

(3)    無引數,也必須保留空的圓括號

(4)    實參列表必須與形參列表一一對應

  1. return 返回值

(1)    如果函式體中包含 return 語句,則結束函式執行並返回值;

(2)    如果函式體中不包含 return 語句,則返回 None 值。

  1. 呼叫函式之前,必須要先定義函式,即先呼叫 def 建立函式物件

(1)    內建函式物件會自動建立

(2)    標準庫和第三方庫函式,通過 import 匯入模組時,會執行模組中的 def 語句

我們通過實際定義函式來學習函式的定義方式。

形參和實參

形參和實參的要點,請參考上一節中的總結。在此不再贅述。

【操作】定義一個函式,實現兩個數的比較,並返回較大的值。

def     printMax(a,b):

'''實現兩個數的比較,並返回較大的值''' if a>b: print(a,'較大值') else:

print(b,'較大值')

printMax(10,20) printMax(30,5)

執行結果:

20 較大值

30 較大值

上面的 printMax 函式中,在定義時寫的 printMax(a,b)。a 和 b 稱為“形式引數”,簡稱“形參”。也就是說,形式引數是在定義函式時使用的。 形式引數的命名只要符合“識別符號”命名規則即可。

在呼叫函式時,傳遞的引數稱為“實際引數”,簡稱“實參”。上面程式碼中, printMax(10,20),10 和 20 就是實際引數。

文件字串(函式的註釋)

程式的可讀性最重要,一般建議在函式體開始的部分附上函式定義說明,這就是“文件字串”,也有人成為“函式的註釋”。我們通過三個單引號或者三個雙引號來實現,中間可以加入多行文字進行說明。

【操作】測試文件字串的使用

def print_star(n):

'''根據傳入的 n,列印多個星號'''

print("*"*n)

help(print_star)

我們呼叫 help(函式名.__doc__)可以列印輸出函式的文件字串。執行結果如下:

Help on function print_star in module __main__:

print_star(n)

根據傳入的 n,列印多個星號

返回值

return 返回值要點:

  1. 如果函式體中包含 return 語句,則結束函式執行並返回值;
  2. 如果函式體中不包含 return 語句,則返回 None 值。
  3. 要返回多個返回值,使用列表、元組、字典、集合將多個值“存起來”即可。

【操作】定義一個列印 n 個星號的無返回值的函式

def print_star(n):

print("*"*n)

print_star(5)

【操作】定義一個返回兩個數平均值的函式

def my_avg(a,b):

return (a+b)/2

#如下是函式的呼叫 c = my_avg(20,30) print(c)

函式也是物件,記憶體底層分析

Python 中,“一切都是物件”。實際上,執行 def 定義函式後,系統就建立了相應的函式物件。我們執行如下程式,然後進行解釋:

def print_star(n): print("*"*n)

print(print_star) print(id(print_star)) c = print_star c(3)

執行結果:

<function print_star at 0x0000000002BB8620>

45844000

***

上面程式碼執行 def 時,系統中會建立函式物件,並通過 print_star 這個變數進行引用:

 

我們執行“c=print_star”後,顯然將 print_star 變數的值賦給了變數 c,記憶體圖變成了:

 

顯然,我們可以看出變數 c 和 print_star 都是指向了同一個函式物件。因此,執行 c(3)和執行 print_star(3)的效果是完全一致的。 Python 中,圓括號意味著呼叫函式。在沒有圓括號的情況下,Python 會把函式當做普通物件。

與此核心原理類似,我們也可以做如下操作: zhengshu = int

zhengshu("234")

顯然,我們將內建函式物件 int()賦值給了變數 zhengshu,這樣 zhengshu 和 int 都是指向了同一個內建函式物件。當然,此處僅限於原理性講解,實際開發中沒必要這麼做。

變數的作用域(全域性變數和區域性變數)

變數起作用的範圍稱為變數的作用域,不同作用域內同名變數之間互不影響。變數分為:全域性變數、區域性變數。

全域性變數:

1. 在函式和類定義之外宣告的變數。作用域為定義的模組,從定義位置開始直到模組結束。

2. 全域性變數降低了函式的通用性和可讀性。應儘量避免全域性變數的使用。

3. 全域性變數一般做常量使用。

4. 函式內要改變全域性變數的值,使用 global 宣告一下區域性變數:

  1. 在函式體中(包含形式引數)宣告的變數。
  2. 區域性變數的引用比全域性變數快,優先考慮使用。
  3. 如果區域性變數和全域性變數同名,則在函式內隱藏全域性變數,只使用同名的區域性變數【操作】全域性變數的作用域測試

a = 100

#全域性變數

def f1():

global a

#如果要在函式內改變全域性變數的值,增加 global 關鍵字宣告

print(a) a = 300

f1() print(a)

#列印全域性變數 a 的值

執行結果:

100

300

【操作】全域性變數和區域性變數同名測試

a=100 def f1():

a = 3

#同名的區域性變數

print(a)

f1() print(a)

#a 仍然是 100,沒有變化

執行結果:

3

100

【操作】 輸出區域性變數和全域性變數

a = 100

def f1(a,b,c):

print(a,b,c) print(locals())

#列印輸出的區域性變數

print("#"*20) print(globals()) f1(2,3,4)

#列印輸出的全域性變數

執行結果:

2 3 4

{'c': 4, 'b': 3, 'a': 2}

####################

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {},

'__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:\\PythonExec\\if_test01.py',

'a': 100, 'f1': <function f1 at 0x0000000002BB8620>}

區域性變數和全域性變數效率測試

區域性變數的查詢和訪問速度比全域性變數快,優先考慮使用,尤其是在迴圈的時候。在特別強調效率的地方或者迴圈次數較多的地方,可以通過將全域性變數轉為區域性變數提高執行速度。

【操作】測試區域性變數和全域性變數效率

#測試區域性變數、全域性變數的效率

import math import time

def test01():

start = time.time() for i in range(10000000):

math.sqrt(30) end = time.time()

print("耗時{0}".format((end-start)))

def test02():

b = math.sqrt start = time.time() for i in range(10000000):

b(30)

end = time.time()

print("耗時{0}".format((end-start)))

test01() test02()

執行結果:

耗時 2.3589999675750732 耗時 1.6410000324249268 引數的傳遞

函式的引數傳遞本質上就是:從實參到形參的賦值操作。 Python 中“一切皆物件”,所有的賦值操作都是“引用的賦值”。所以,Python 中引數的傳遞都是“引用傳遞”,不是“值傳遞”。具體操作時分為兩類:

1. 對“可變物件”進行“寫操作”,直接作用於原物件本身。

2. 對“不可變物件”進行“寫操作”,會產生一個新的“物件空間”,並用新的值填充這塊空間。(起到其他語言的“值傳遞”效果,但不是“值傳遞”)

可變物件有:

字典、列表、集合、自定義的物件等不可變物件有:

數字、字串、元組、function 等傳遞可變物件的引用

傳遞引數是可變物件(例如:列表、字典、自定義的其他可變物件等),實際傳遞的還是物件的引用。在函式體中不建立新的物件拷貝,而是可以直接修改所傳遞的物件。

【操作】引數傳遞:傳遞可變物件的引用

b = [10,20] def f2(m):

           print("m:",id(m))           #b 和 m 是同一個物件

          m.append(30) #由於 m 是可變物件,不建立物件拷貝,直接修改這個物件

f2(b)

print("b:",id(b)) print(b)

執行結果:

m: 45765960

b: 45765960

[10, 20, 30]

傳遞不可變物件的引用

傳遞引數是不可變物件(例如:int、float、字串、元組、布林值),實際傳遞的還是物件的引用。在”賦值操作”時,由於不可變物件無法修改,系統會新建立一個物件。

【操作】引數傳遞:傳遞不可變物件的引用

a = 100 def f1(n):

print("n:",id(n))

#傳遞進來的是 a 物件的地址

n = n+200

#由於 a 是不可變物件,因此建立新的物件 n

print("n:",id(n)) print(n)

f1(a)

print("a:",id(a))

#n 已經變成了新的物件

執行結果:

n: 1663816464 n: 46608592

300

a: 1663816464

顯然,通過 id 值我們可以看到 n 和 a 一開始是同一個物件。給 n 賦值後,n 是新的物件。

淺拷貝和深拷貝

為了更深入的瞭解引數傳遞的底層原理,我們需要講解一下“淺拷貝和深拷貝”。我們可以使用內建函式:copy(淺拷貝)、deepcopy(深拷貝)。

淺拷貝:不拷貝子物件的內容,只是拷貝子物件的引用。

深拷貝:會連子物件的記憶體也全部拷貝一份,對子物件的修改不會影響源物件

原始碼:

#測試淺拷貝和深拷貝

import copy

def testCopy():

'''測試淺拷貝''' a = [10, 20, [5, 6]] b = copy.copy(a)

print("a", a) print("b", b) b.append(30)

b[2].append(7) print("淺拷貝......") print("a", a) print("b", b)

def testDeepCopy():

'''測試深拷貝''' a = [10, 20, [5, 6]] b = copy.deepcopy(a)

print("a", a) print("b", b) b.append(30)

b[2].append(7) print("深拷貝......") print("a", a) print("b", b)

testCopy()

print("*************") testDeepCopy()

執行結果:

a [10, 20, [5, 6]] b [10, 20, [5, 6]] 淺拷貝...... a [10, 20, [5, 6, 7]] b [10, 20, [5, 6, 7], 30]

*************

a [10, 20, [5, 6]] b [10, 20, [5, 6]] 深拷貝...... a [10, 20, [5, 6]] b [10, 20, [5, 6, 7], 30]

傳遞不可變物件包含的子物件是可變的情況

#傳遞不可變物件時。不可變物件裡面包含的子物件是可變的。則方法內修改了這個可變物件,源物件也發生了變化。

a = (10,20,[5,6]) print("a:",id(a))

def test01(m): print("m:",id(m)) m[2][0] = 888 print(m) print("m:",id(m))

test01(a) print(a)

執行結果:

a: 41611632 m: 41611632 (10, 20, [888, 6]) m: 41611632 (10, 20, [888, 6])

引數的幾種型別位置引數

函式呼叫時,實參預設按位置順序傳遞,需要個數和形參匹配。按位置傳遞的引數,稱為: “位置引數”。【操作】測試位置引數

def f1(a,b,c): print(a,b,c)

f1(2,3,4)

f1(2,3)         #報錯,位置引數不匹配

執行結果:

2 3 4

Traceback (most recent call last):

File "E:\PythonExec\if_test01.py", line 5, in <module> f1(2,3)

TypeError: f1() missing 1 required positional argument: 'c'

預設值引數

我們可以為某些引數設定預設值,這樣這些引數在傳遞時就是可選的。稱為“預設值引數”。預設值引數放到位置引數後面。【操作】測試預設值引數

def f1(a,b,c=10,d=20):

print(a,b,c,d)

f1(8,9) f1(8,9,19) f1(8,9,19,29)

#預設值引數必須位於普通位置引數後面

執行結果:

8 9 10 20

8 9 19 20

8 9 19 29 命名引數

我們也可以按照形參的名稱傳遞引數,稱為“命名引數”,也稱“關鍵字引數”。

【操作】測試命名引數

def f1(a,b,c):

print(a,b,c)

f1(8,9,19)                  #位置引數

f1(c=10,a=20,b=30)     #命名引數

執行結果:

8 9 19

20 30 10 可變引數

可變引數指的是“可變數量的引數”。分兩種情況:

  1. *param(一個星號),將多個引數收集到一個“元組”物件中。
  2. **param(兩個星號),將多個引數收集到一個“字典”物件中。

【操作】測試可變引數處理(元組、字典兩種方式)

def f1(a,b,*c):

print(a,b,c)

f1(8,9,19,20)

def f2(a,b,**c):

print(a,b,c)

f2(8,9,name='gaoqi',age=18)

def     f3(a,b,*c,**d):

print(a,b,c,d) f3(8,9,20,30,name='gaoqi',age=18)

執行結果:

8 9 (19, 20)

8 9 {'name': 'gaoqi', 'age': 18}

8 9 (20, 30) {'name': 'gaoqi', 'age': 18} 強制命名引數

在帶星號的“可變引數”後面增加新的引數,必須在呼叫的時候“強制命名引數”。

【操作】強制命名引數的使用

def f1(*a,b,c):

print(a,b,c)

#f1(2,3,4) #會報錯。由於 a 是可變引數,將 2,3,4 全部收集。造成 b 和 c 沒有賦值。

f1(2,b=3,c=4)

執行結果:

(2,) 3 4

lambda 表示式和匿名函式

lambda 表示式可以用來宣告匿名函式。lambda 函式是一種簡單的、在同一行中定義函式的方法。lambda 函式實際生成了一個函式物件。

lambda 表示式只允許包含一個表示式,不能包含複雜語句,該表示式的計算結果就是函式的返回值。

lambda 表示式的基本語法如下:

                                                 lambda     arg1,arg2,arg3...    :   <表示式>

arg1/arg2/arg3 為函式的引數。<表示式>相當於函式體。運算結果是:表示式的運算結果。

【操作】lambda 表示式使用

f   = lambda a,b,c:a+b+c

print(f) print(f(2,3,4))

g  = [lambda a:a*2,lambda b:b*3,lambda c:c*4]

print(g[0](6),g[1](7),g[2](8))

執行結果:

<function <lambda> at 0x0000000002BB8620>

9

12 21 32

eval()函式

功能:將字串 str 當成有效的表示式來求值並返回計算結果。

語法: eval(source[, globals[, locals]]) -> value

引數:

source:一個 Python 表示式或函式 compile()返回的程式碼物件

globals:可選。必須是 dictionary locals:可選。任意對映物件

#測試eval()函式

s = "print('abcde')" eval(s)

a = 10 b = 20

c = eval("a+b")

print(c)

dict1 = dict(a=100,b=200)

d = eval("a+b",dict1) print(d)

eval 函式會將字串當做語句來執行,因此會被注入安全隱患。比如:字串中含有刪除檔案的語句。那就麻煩大了。因此,使用時候,要慎重!!!遞迴函式

遞迴函式指的是:自己呼叫自己的函式,在函式體內部直接或間接的自己呼叫自己。遞迴類似於大家中學數學學習過的“數學歸納法”。 每個遞迴函式必須包含兩個部分:

  1. 終止條件

表示遞迴什麼時候結束。一般用於返回值,不再呼叫自己。

  1. 遞迴步驟

把第 n 步的值和第 n-1 步相關聯。

遞迴函式由於會建立大量的函式物件、過量的消耗記憶體和運算能力。在處理大量資料時,謹慎使用。

【操作】 使用遞迴函式計算階乘(factorial)

def factorial(n):

if n==1:return 1 return n*factorial(n-1)

for i in range(1,6):

print(i,'!=',factorial(i))

執行結果:

1  != 1

2  != 2

3  != 6

4  != 24

5!=120

 

巢狀函式(內部函式)

巢狀函式:

在函式內部定義的函式!

【操作】巢狀函式定義

def     f1():

print('f1 running...')

def f2(): print('f2 running...') f2() f1()

執行結果:

f1 running... f2 running...

上面程式中,f2()就是定義在 f1 函式內部的函式。f2()的定義和呼叫都在 f1()函式內部。一般在什麼情況下使用巢狀函式?

  1. 封裝 - 資料隱藏

外部無法訪問“巢狀函式”。

  1. 貫徹 DRY(Don’t     Repeat  Yourself) 原則

巢狀函式,可以讓我們在函式內部避免重複程式碼。

  1. 閉包

後面會詳細講解。

【操作】使用巢狀函式避免重複程式碼

def printChineseName(name,familyName): print("{0} {1}".format(familyName,name))

def printEnglishName(name,familyName):

print("{0} {1}".format(name, familyName))

#使用1個函式代替上面的兩個函式

def printName(isChinese,name,familyName):

def inner_print(a,b):

print("{0} {1}".format(a,b))

if isChinese:

inner_print(familyName,name)

else:

inner_print(name,familyName)

printName(True,"小七","高") printName(False,"George","Bush")

nonlocal 關鍵字

nonlocal      用來宣告外層的區域性變數。

global           用來宣告全域性變數。

【操作】使用 nonlocal 宣告外層區域性變數

#測試nonlocal、global關鍵字的用法 a = 100

def outer(): b = 10

def inner():

                       nonlocal b          #宣告外部函式的區域性變數

print("inner b:",b)

b = 20

                      global a            #宣告全域性變數

a = 1000

inner()

print("outer b:",b)

outer() print("a:",a)

LEGB 規則

Python 在查詢“名稱”時,是按照 LEGB 規則查詢的:

Local-->Enclosed-->Global-->Built in

          Local          指的就是函式或者類的方法內部

          Enclosed    指的是巢狀函式(一個函式包裹另一個函式,閉包)

          Global       指的是模組中的全域性變數

          Built in      指的是 Python 為自己保留的特殊名稱。

如果某個 name 對映在區域性(local)名稱空間中沒有找到,接下來就會在閉包作用域 (enclosed)進行搜尋,如果閉包作用域也沒有找到,Python 就會到全域性(global)名稱空間中進行查詢,最後會在內建(built-in)名稱空間搜尋 (如果一個名稱在所有名稱空間中都沒有找到,就會產生一個 NameError)。

#測試 LEGB

str = "global" def outer(): str = "outer"

def inner(): str = "inner"

print(str)

inner()

outer()

我們依次將幾個 str 註釋掉,觀察控制檯列印的內容,體會 LEBG 的搜尋順序。

實操作業

  1. 定義一個函式實現反響輸出一個整數。比如:輸入 3245,輸出 5432.
  2. 編寫一個函式,計算下面的數列:

m(n)  1 2 ... n

                          2    3           n1

  1. 輸入三角形三個頂點的座標,若有效則計算三角形的面積;如座標無效,則給出提示。
  2. 輸入一個毫秒數,將該數字換算成小時數,分鐘數、秒數。
  3. 使用海龜繪圖。輸入多個點,將這些點都兩兩相連。

第6章(1) 物件導向初步

物件導向(Object oriented Programming,OOP)程式設計的思想主要是針對大型軟體設計而來的。物件導向程式設計使程式的擴充套件性更強、可讀性更好,使的程式設計可以像搭積木一樣簡單。

物件導向程式設計將資料和運算元據相關的方法封裝到物件中,組織程式碼和資料的方式更加接近人的思維,從而大大提高了程式設計的效率。

Python 完全採用了物件導向的思想,是真正物件導向的程式語言,完全支援物件導向的基本功能,例如:繼承、多型、封裝等。

Python 中,一切皆物件。我們在前面學習的資料型別、函式等,都是物件。

注:Python 支援程式導向、物件導向、函數語言程式設計等多種程式設計正規化。

物件導向和麵向過程區別

·程式導向(Procedure Oriented)思維

程式導向程式設計更加關注的是“程式的邏輯流程”,是一種“執行者”思維,適合編寫小規模的程式。

程式導向思想思考問題時,我們首先思考“怎麼按步驟實現?”並將步驟對應成方法,一步一步,最終完成。 這個適合簡單任務,不需要過多協作的情況下。比如,如何開車?我們很容易就列出實現步驟:

         1. 發動車      2. 掛擋         3.踩油門       4. 走你

程式導向適合簡單、不需要協作的事務。 但是當我們思考比較複雜的問題,比如“如何造車?”,就會發現列出 1234 這樣的步驟,是不可能的。那是因為,造車太複雜,需要很多協作才能完成。此時物件導向思想就應運而生了。

·物件導向(Object Oriented)思維

物件導向更加關注的是“軟體中物件之間的關係”,是一種“設計者”思維,適合編寫大規模的程式。

物件導向(Object)思想更契合人的思維模式。我們首先思考的是“怎麼設計這個事物?” 比如思考造車,我們就會先思考“車怎麼設計?”,而不是“怎麼按步驟造車的問題”。這就是思維方式的轉變。

q 物件導向方式思考造車,發現車由如下物件組成:

  1. 1.     輪胎
  2. 2.     發動機
  3. 3.     車殼
  4. 4.     座椅
  5. 5.     擋風玻璃

為了便於協作,我們找輪胎廠完成製造輪胎的步驟,發動機廠完成製造發動機的步驟;這樣,發現大家可以同時進行車的製造,最終進行組裝,大大提高了效率。但是,具體到輪胎廠的一個流水線操作,仍然是有步驟的,還是離不開程式導向思想!

因此,物件導向可以幫助我們從巨集觀上把握、從整體上分析整個系統。 但是,具體到實現部分的微觀操作(就是一個個方法),仍然需要程式導向的思路去處理。

我們千萬不要把程式導向和麵向物件對立起來。他們是相輔相成的。物件導向離不開程式導向!

· 物件導向思考方式

遇到複雜問題,先從問題中找名詞(程式導向更多的是找動詞),然後確立這些名詞哪些可以作為類,再根據問題需求確定的類的屬性和方法,確定類之間的關係。

· 物件導向和麵向過程的總結

q  都是解決問題的思維方式,都是程式碼組織的方式。

q  解決簡單問題可以使用程式導向

q  解決複雜問題:巨集觀上使用物件導向把握,微觀處理上仍然是程式導向。

 

一個好的“設計者”肯定也是“好的執行者”,不然無法落地,白忙一場。

一個好的“執行者”不一定是“好的設計者”,眼界層次不夠,越忙越亂。

 

物件的進化

隨著程式設計面臨的問題越來越複雜,程式語言本身也在進化,從主要處理簡單資料開始,隨著資料變多進化“陣列”; 資料型別變複雜,進化出了“結構體”; 處理資料的方式和邏輯變複雜,進化出了“物件”。

  1. 簡單資料

像 30,40,50.4 等這些數字,可以看做是簡單資料。最初的計算機程式設計,都是像這樣的數字。

  1. 陣列

將同型別的資料放到一起。比如:整數陣列[20,30,40],浮點數陣列[10.2, 11.3, 12.4],字串陣列:[“aa”,”bb”,”cc”]

  1. 結構體

將不同型別的資料放到一起,是 C 語言中的資料結構。比如:

struct resume{ int age;

char name[10];

double salary;

};

  1. 物件

將不同型別的資料、方法(即函式)放到一起,就是物件。比如:

class Student:

           company = "SXT"         #類屬性

count = 0      #類屬性 def __init__(self,name,score):

                    self.name = name                #例項屬性

self.score = score

Student.count = Student.count+1

           def say_score(self):                    #例項方法

print("我的公司是:",Student.company) print(self.name,'的分數是:',self.score)

我們前面學習的數字也是物件。比如:整數 9,就是一個包含了加法、乘法等方法的物件。類的定義我們把物件比作一個“餅乾”,類就是製造這個餅乾的“模具”。

 

我們通過類定義資料型別的屬性(資料)和方法(行為),也就是說,“類將行為和狀態打包在一起”。

 

物件是類的具體實體,一般稱為“類的例項”。類看做“餅乾模具”,物件就是根據這個“模具”製造出的“餅乾”。

從一個類建立物件時,每個物件會共享這個類的行為(類中定義的方法),但會有自己的屬性值(不共享狀態)。更具體一點:“方法程式碼是共享的,屬性資料不共享”。

 

Python 中,“一切皆物件”。類也稱為“類物件”,類的例項也稱為“例項物件”。

定義類的語法格式如下: class      類名:類體

要點如下:

  1. 類名必須符合“識別符號”的規則;一般規定,首字母大寫,多個單詞使用“駝峰原則”。
  2. 類體中我們可以定義屬性和方法。
  3. 屬性用來描述資料,方法(即函式)用來描述這些資料相關的操作。

【操作】一個典型的類的定義

class Student:

def __init__(self,name,score):     #構造方法第一個引數必須為 self self.name = name #例項屬性

self.score = score

             def say_score(self):                    #例項方法

print(self.name,'的分數是:',self.score)

s1 = Student('張三',80) s1.say_score()

#s1 是例項物件,自動呼叫__init__()方法

__init__構造方法和__new__方法

類是抽象的,也稱之為“物件的模板”。我們需要通過類這個模板,建立類的例項物件,然後才能使用類定義的功能。

我們前面說過一個 Python 物件包含三個部分:id(identity 識別碼)、type(物件型別)、 value(物件的值)。

現在,我們可以更進一步的說,一個 Python 物件包含如下部分:

  1. id(identity 識別碼)
  2. type(物件型別)
  3. value(物件的值)

(1)   屬性(attribute) (2) 方法(method)

建立物件,我們需要定義建構函式__init__()方法。構造方法用於執行“例項物件的初始化工作”,即物件建立後,初始化當前物件的相關屬性,無返回值。

__init__()的要點如下:

  1. 名稱固定,必須為:__init__()
  2. 第一個引數固定,必須為:self。     self 指的就是剛剛建立好的例項物件。
  3. 建構函式通常用來初始化例項物件的例項屬性,如下程式碼就是初始化例項屬性:name 和 score。

def __init__(self,name,score): self.name = name self.score = score

#例項屬性

  1. 通過“類名(引數列表)”來呼叫建構函式。呼叫後,將建立好的物件返回給相應的變數。

比如:s1 = Student('張三', 80)

  1. __init__()方法:初始化建立好的物件,初始化指的是:“給例項屬性賦值”
  2. __new__()方法: 用於建立物件,但我們一般無需重定義該方法。
  3. 如果我們不定義__init__方法,系統會提供一個預設的__init__方法。如果我們定義了帶參的__init__方法,系統不建立預設的__init__方法。

注:

1. Python 中的 self 相當於 C++中的 self 指標,JAVA 和 C#中的 this 關鍵字。Python 中, self 必須為建構函式的第一個引數,名字可以任意修改。但一般遵守慣例,都叫做 self。

例項屬性和例項方法

例項屬性

例項屬性是從屬於例項物件的屬性,也稱為“例項變數”。他的使用有如下幾個要點:

  1. 例項屬性一般在__init__()方法中通過如下程式碼定義: self.例項屬性名 = 初始值
  2. 在本類的其他例項方法中,也是通過 self 進行訪問: self.例項屬性名
  3. 建立例項物件後,通過例項物件訪問:

                   obj01 = 類名()          #建立物件,呼叫__init__()初始化屬性

obj01.例項屬性名 = 值    #可以給已有屬性賦值,也可以新加屬性例項方法

例項方法是從屬於例項物件的方法。例項方法的定義格式如下: def  方法名(self [, 形參列表]):函式體

方法的呼叫格式如下:

物件.方法名([實參列表])

要點:

  1. 定義例項方法時,第一個引數必須為 self。和前面一樣,self 指當前的例項物件。
  2. 呼叫例項方法時,不需要也不能給 self 傳參。self 由直譯器自動傳參。

· 函式和方法的區別

  1. 都是用來完成一個功能的語句塊,本質一樣。
  2. 方法呼叫時,通過物件來呼叫。方法從屬於特定例項物件,普通函式沒有這個特點。
  3. 直觀上看,方法定義時需要傳遞 self,函式不需要。

· 例項物件的方法呼叫本質:

 

  1. dir(obj)可以獲得物件的所有屬性、方法
  2. obj.__dict__   物件的屬性字典
  3. pass 空語句
  4. isinstance(物件,型別)   判斷“物件”是不是“指定型別” 類物件、類屬性、類方法、靜態方法

類物件

我們在前面講的類定義格式中,“class  類名:”。實際上,當直譯器執行 class 語句時,就會建立一個類物件。

【操作】測試類物件的生成

class Student:

          pass       #空語句

print(type(Student)) print(id(Student))

Stu2 = Student s1 = Stu2() print(s1)

執行結果如下:

<class 'type'>

51686328

<__main__.Student object at 0x0000000002B5FDD8>

我們可以看到實際上生成了一個變數名就是類名“Student”的物件。我們通過賦值給新變數 Stu2,也能實現相關的呼叫。說明,確實建立了“類物件”。

【注】pass 為空語句。就是表示什麼都不做,只是作為一個佔位符存在。當你寫程式碼時,遇到暫時不知道往方法或者類中加入什麼時,可以先用 pass 佔位,後期再補上。

類屬性

類屬性是從屬於“類物件”的屬性,也稱為“類變數”。由於,類屬性從屬於類物件,可以被所有例項物件共享。

類屬性的定義方式:

                                      class   類名:

類變數名= 初始值

在類中或者類的外面,我們可以通過:“類名.類變數名”來讀寫。

【操作】 類屬性的使用測試

class Student:

           company = "SXT"         #類屬性

          count = 0                   #類屬性

def __init__(self,name,score):

                    self.name = name                #例項屬性

self.score = score

Student.count = Student.count+1

           def say_score(self):                    #例項方法

print("我的公司是:",Student.company) print(self.name,'的分數是:',self.score)

s1 = Student('張三',80)      #s1 是例項物件,自動呼叫__init__()方法 s1.say_score()

print('一共建立{0}個 Student 物件'.format(Student.count))

執行結果:我的公司是: SXT

張三 的分數是: 80

一共建立 1 個 Student 物件

類方法

類方法是從屬於“類物件”的方法。類方法通過裝飾器@classmethod 來定義,格式如下:

@classmethod

def  類方法名(cls [,形參列表]) :函式體要點如下:

  1. @classmethod 必須位於方法上面一行
  2. 第一個 cls 必須有;cls 指的就是“類物件”本身;
  3. 呼叫類方法格式:“類名.類方法名(引數列表)”。 引數列表中,不需要也不能給 cls 傳值。
  4. 類方法中訪問例項屬性和例項方法會導致錯誤
  5. 子類繼承父類方法時,傳入 cls 是子類物件,而非父類物件

【操作】類方法使用測試

class Student:

company = "SXT" @classmethod

#類屬性

def printCompany(cls): print(cls.company)

Student.printCompany()

靜態方法

Python 中允許定義與“類物件”無關的方法,稱為“靜態方法”。

“靜態方法”和在模組中定義普通函式沒有區別,只不過“靜態方法”放到了“類的名字空間裡面”,需要通過“類呼叫”。

靜態方法通過裝飾器@staticmethod 來定義,格式如下:

@staticmethod

def  靜態方法名([形參列表]) :函式體要點如下:

  1. @staticmethod 必須位於方法上面一行
  2. 呼叫靜態方法格式:“類名.靜態方法名(引數列表)”。
  3. 靜態方法中訪問例項屬性和例項方法會導致錯誤

【操作】靜態方法使用測試

class Student:

company = "SXT" #類屬性

@staticmethod

def add(a, b): #靜態方法

print("{0}+{1}={2}".format(a,b,(a+b))) return a+b

Student.add(20,30)

記憶體分析例項物件和類物件建立過程(重要)我們以下面程式碼為例,分析整個建立過程,讓大家對物件導向概念掌握更加深刻:

class Student:

company = "尚學堂"  #類屬性 count = 0     #類屬性

def __init__(self,name,score):

                    self.name = name                #例項屬性

self.score = score

Student.count = Student.count+1

def say_score(self):                            #例項方法

print("我的公司是:",Student.company) print(self.name,'的分數是:',self.score)

s1 = Student('高淇',80)      #s1 是例項物件,自動呼叫__init__()方法 s1.say_score()

print('一共建立{0}個 Student 物件'.format(Student.count))

 

__del__方法(解構函式)和垃圾回收機制

__del__方法稱為“析構方法”,用於實現物件被銷燬時所需的操作。比如:釋放物件佔用的資源,例如:開啟的檔案資源、網路連線等。

Python 實現自動的垃圾回收,當物件沒有被引用時(引用計數為 0),由垃圾回收器呼叫__del__方法。

我們也可以通過 del 語句刪除物件,從而保證呼叫__del__方法。

系統會自動提供__del__方法,一般不需要自定義析構方法。

#解構函式

class Person:

def __del__(self):

print("銷燬物件:{0}".format(self))

p1 = Person() p2 = Person() del p2 print("程式結束")

運算結果:

銷燬物件:<__main__.Person object at 0x02175610> 程式結束

銷燬物件:<__main__.Person object at 0x021755D0>

__call__方法和可呼叫物件

定義了__call__方法的物件,稱為“可呼叫物件”,即該物件可以像函式一樣被呼叫。

#測試__call__,可呼叫物件

class SalaryAccount:

'''工資計算類'''

def __call__(self, salary): yearSalary = salary*12 daySalary = salary//30 hourSalary = daySalary//8

return

dict(monthSalary=salary,yearSalary=yearSalary,daySalary=daySalary

,hourSalary=hourSalary) s = SalaryAccount()

print(s(5000))              #可以像呼叫函式一樣呼叫物件的__call__方法

執行結果:

{'monthSalary': 5000, 'yearSalary': 60000, 'daySalary': 166, 'hourSalary': 20}

方法沒有過載

在其他語言中,可以定義多個重名的方法,只要保證方法簽名唯一即可。方法簽名包含 3 個部分:方法名、引數數量、引數型別。

Python 中,方法的的引數沒有宣告型別(呼叫時確定引數的型別),引數的數量也可以由可變引數控制。因此,Python 中是沒有方法的過載的。定義一個方法即可有多種呼叫方式,相當於實現了其他語言中的方法的過載。

如果我們在類體中定義了多個重名的方法,只有最後一個方法有效。

建議:不要使用重名的方法!Python 中方法沒有過載。

#Python中沒有方法的過載。定義多個同名方法,只有最後一個有效

class Person:

def say_hi(self):

print("hello")

def say_hi(self,name):

print("{0},hello".format(name))

p1 = Person()

#p1.say_hi()     #不帶參,報錯:TypeError:say_hi()missing1 requiredpositionalargument:'name' p1.say_hi("高淇")

方法的動態性

Python 是動態語言,我們可以動態的為類新增新的方法,或者動態的修改類的已有的方法。

#測試方法的動態性 class Person:

def work(self):

print("努力上班!")

def play_game(self):

print("{0}玩遊戲".format(self))

def work2(s):

print("好好工作,努力上班!")

Person.play = play_game Person.work = work2 p = Person() p.play() p.work()

我們可以看到,Person 動態的新增了 play_game 方法,以及用 work2 替換了 work 方法。

私有屬性和私有方法(實現封裝)

Python 對於類的成員沒有嚴格的訪問控制限制,這與其他面嚮物件語言有區別。關於私有屬性和私有方法,有如下要點:

  1. 通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public)。
  2. 類內部可以訪問私有屬性(方法)
  3. 類外部不能直接訪問私有屬性(方法)
  4. 類外部可以通過“_類名__私有屬性(方法)名”訪問私有屬性(方法)

【注】方法本質上也是屬性!只不過是可以通過()執行而已。所以,此處講的私有屬性和公有屬性,也同時講解了私有方法和公有方法的用法。如下測試中,同時也包含了私有方法和公有方法的例子。

【測試】私有屬性和公有屬性使用測試

#測試私有屬性、私有方法 class Employee:

__company = "百戰程式設計師"

#私有類屬性. 通過dir可以查到

_Employee__company

def __init__(self,name,age): self.name = name self.__age = age

#私有例項屬性

 

def say_company(self):

                           print("我的公司是:",Employee.__company)                       #類內部

可以直接訪問私有屬性

print(self.name,"的年齡是:",self.__age) self.__work()

               def __work(self):                     #私有例項方法 通過dir可以查到

_Employee__work

print("工作!好好工作,好好賺錢,娶個媳婦!")

p1 = Employee("高淇",32) print(p1.name)

print(dir(p1))                                 #

p1.say_company()

print(p1._Employee__age)                  #通過這種方式可以直接訪問到私有屬

性 。通過dir可以查到屬性:_Employee__age

#print(p1.__age)                    #直接訪問私有屬性,報錯

#p1.__sleep()                       #直接訪問私有方法,報錯

執行結果:

高淇

['_Person__age',       '_Person__leg_num',      '_Person__sleep',     '__class__',        '__delattr__',

'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',

'__subclasshook__', '__weakref__', 'name', 'say_age']

腿的數目: 2 高淇 的年齡是: 18

睡覺

18

從列印的 Person 物件所有屬性我們可以看出。私有屬性“__age”在實際儲存時是按照 “_Person__age”這個屬性來儲存的。這也就是為什麼我們不能直接使用“__age”而可以使用“_Person__age”的根本原因。

@property 裝飾器

@property 可以將一個方法的呼叫方式變成“屬性呼叫”。下面是一個簡單的示例,讓大家體會一下這種轉變:

#簡單測試@property class Employee:

@property

def salary(self): return 30000;

emp1 = Employee()

print(emp1.salary)                                 #列印30000

print(type(emp1.salary))                       #列印<class'int'>

#emp1.salary()                                       #報錯:TypeError:'int'objectisnot

callable

#emp1.salary=1000                           #@property修飾的屬性,如果沒有

加setter方法,則為只讀屬性。此處修改報錯:AttributeError:can'tset attribute

@property 主要用於幫助我們處理屬性的讀操作、寫操作。對於某一個屬性,我們可以直接通過:

emp1.salary = 30000

如上的操作讀操作、寫操作。但是,這種做法不安全。比如,我需要限制薪水必須為 1-10000 的數字。這時候,我們就需要通過 getter、setter 方法來處理。

#測試@property class Employee:

def __init__(self,name,salary): self.name = name

self.__salary = salary

@property  #相當於salary屬性的getter方法 def salary(self):

print("月薪為{0},年薪為

{1}".format(self.__salary,(12*self.__salary))) return self.__salary;

@salary.setter

def salary(self,salary): #相當於salary屬性的setter方法 if(0<salary<1000000):

self.__salary = salary

else:

print("薪水錄入錯誤!只能在 0-1000000 之間")

emp1 = Employee("高淇",100)

print(emp1.salary) emp1.salary = -200

執行結果:

月薪為 100,年薪為 1200

100

月薪為 100,年薪為 1200

100

薪水錄入錯誤!只能在 0-1000000 之間

屬性和方法命名總結

· _xxx:保護成員,不能用“from module import * ”匯入,只有類物件和子類物件能訪問這些成員。

· __xxx__:系統定義的特殊成員

· __xxx: 類中的私有成員,只有類物件自己能訪問,子類物件也不能訪問。(但,在類外部可以通過“物件名. _類名__xxx”這種特殊方式訪問。Python 不存在嚴格意義的私有成員)

注:再次強調,方法和屬性都遵循上面的規則。

類編碼風格

  1. 類名首字母大寫,多個單詞之間採用駝峰原則。
  2. 例項名、模組名採用小寫,多個單詞之間採用下劃線隔開。
  3. 每個類,應緊跟“文件字串”,說明這個類的作用。
  4. 可以用空行組織程式碼,但不能濫用。在類中,使用一個空行隔開方法;模組中,使用兩個空行隔開多個類。

實操作業

  1. 如下程式碼,使用圖文分析整個記憶體過程:

class Student:

company = "尚學堂"  #類屬性 count = 0     #類屬性

def __init__(self,name,score):

                    self.name = name                #例項屬性

self.score = score

Student.count = Student.count+1

           def say_score(self):                    #例項方法

print("我的公司是:",Student.company) print(self.name,'的分數是:',self.score)

s1 = Student('高淇',80)   #s1 是例項物件,自動呼叫__init__()方法 s1.say_score()

print('一共建立{0}個 Student 物件'.format(Student.count))

  1. 設計一個名為 MyRectangle 的矩形類來表示矩形。這個類包含:

(1)    左上角頂點的座標:x,y

(2)    寬度和高度:width、height

(3)    構造方法:傳入 x,y,width,height。如果(x,y)不傳則預設是 0,如果 width 和 height 不傳,則預設是 100.

(4)    定義一個 getArea() 計算面積的方法

(5)    定義一個 getPerimeter(),計算周長的方法

(6)    定義一個 draw()方法,使用海龜繪圖繪製出這個矩形

第6章(2) 物件導向進階

物件導向三大特徵介紹

Python 是物件導向的語言,也支援物件導向程式設計的三大特性:繼承、封裝(隱藏)、多型。

·封裝(隱藏)

隱藏物件的屬性和實現細節,只對外提供必要的方法。相當於將“細節封裝起來”,只對外暴露“相關呼叫方法”。

通過前面學習的“私有屬性、私有方法”的方式,實現“封裝”。Python 追求簡潔的語法,沒有嚴格的語法級別的“訪問控制符”,更多的是依靠程式設計師自覺實現。

·繼承

繼承可以讓子類具有父類的特性,提高了程式碼的重用性。

從設計上是一種增量進化,原有父類設計不變的情況下,可以增加新的功能,或者改進已有的演算法。

·多型

多型是指同一個方法呼叫由於物件不同會產生不同的行為。生活中這樣的例子比比皆是:同樣是休息方法,人不同休息方法不同。張三休息是睡覺,李四休息是玩遊戲,程式設計師休息是“敲幾行程式碼”。

繼承

繼承是物件導向程式設計的重要特徵,也是實現“程式碼複用”的重要手段。

如果一個新類繼承自一個設計好的類,就直接具備了已有類的特徵,就大大降低了工作難度。已有的類,我們稱為“父類或者基類”,新的類,我們稱為“子類或者派生類”。

 

語法格式

Python 支援多重繼承,一個子類可以繼承多個父類。繼承的語法格式如下: class 子類類名(父類 1[,父類 2,...]):類體

如果在類定義中沒有指定父類,則預設父類是 object 類。也就是說,object 是所有類的父類,裡面定義了一些所有類共有的預設實現,比如:__new__()。

定義子類時,必須在其建構函式中呼叫父類的建構函式。呼叫格式如下:父類名.__init__(self, 引數列表)

class Person:

def __init__(self,name,age): self.name = name

self.__age = age

def say_age(self): print(self.name,"的年齡是:",self.__age)

class Student(Person):

def __init__(self,name,age,score):

self.score = score

Person.__init__(self,name,age) #建構函式中包含呼叫父類構

造函式。根據需要,不是必須。子類並不會自動呼叫父類的__init__(),我們必須顯式的呼叫它。

s1 = Student("張三",15,85) s1.say_age() print(dir(s1))

執行結果:

張三 的年齡是: 15

['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',

'__weakref__', 'name', 'say_age', 'score']

類成員的繼承和重寫

  1. 成員繼承:子類繼承了父類除構造方法之外的所有成員。
  2. 方法重寫:子類可以重新定義父類中的方法,這樣就會覆蓋父類的方法,也稱為“重寫”

【操作】繼承和重寫的案例

class Person:

def __init__(self,name,age): self.name = name self.age = age

def say_age(self): print(self.name,"的年齡是:",self.age)

def say_name(self): print("我是",self.name)

class Student(Person):

def __init__(self,name,age,score):

self.score = score

                     Person.__init__(self,name,age)   #建構函式中包含呼叫父類建構函式

def say_score(self):

print(self.name,"的分數是:",self.score)

def say_name(self):     #重寫父類的方法 print("報告老師,我是",self.name)

s1 = Student("張三",15,85) s1.say_score() s1.say_name() s1.say_age()

執行結果:張三 的分數是: 85 報告老師,我是 張三張三 的年齡是: 15

檢視類的繼承層次結構通過類的方法 mro()或者類的屬性__mro__可以輸出這個類的繼承層次結構。【操作】 檢視類的繼承層次結構

class A:pass class B(A):pass class C(B):pass print(C.mro())

執行結果:

[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

 

object 根類

object 類是所有類的父類,因此所有的類都有 object 類的屬性和方法。我們顯然有必要深入研究一下 object 類的結構。對於我們繼續深入學習 Python 很有好處。

dir()檢視物件屬性

為了深入學習物件,我們先學習內建函式 dir(),他可以讓我們方便的看到指定物件所有的屬性。

【測試】檢視物件所有屬性以及和 object 進行比對

class Person:

def __init__(self,name,age): self.name = name self.age = age

def say_age(self):

print(self.name,"的年齡是:",self.age)

obj = object()

print(dir(obj))

s2 = Person("高淇",18)

print(dir(s2))

執行結果:

['__class__',      '__delattr__',     '__dir__',     '__doc__',     '__eq__',     '__format__',       '__ge__',

'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',

'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',

'__str__', '__subclasshook__']

 

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',

'__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age']

從上面我們可以發現這樣幾個要點:

  1. Person 物件增加了六個屬性:

                        __dict__      __module__     __weakref__      age      name      say_age

  1. object 的所有屬性,Person 類作為 object 的子類,顯然包含了所有的屬性。
  2. 我們列印 age、name、say_age,發現 say_age 雖然是方法,實際上也是屬性。只不過,這個屬性的型別是“method”而已。 age <class 'int'> name <class 'str'> say_age <class 'method'>

【注】關於 object 這些屬性的詳細學習,會在後面學習中逐個涉及。在此,無法一一展開。

重寫__str__()方法

object 有一個__str__()方法,用於返回一個對於“物件的描述”,對應於內建函式 str() 經常用於 print()方法,幫助我們檢視物件的資訊。__str__()可以重寫。

class Person:

def __init__(self,name,age): self.name = name self.__age = age

def __str__(self):

'''將物件轉化成一個字串,一般用於print方法'''

return "名字是:{0},年齡是{1}".format(self.name,self.__age)

p = Person("高淇",18)

print(p)

執行結果:名字是:高淇,年齡是 18

多重繼承

Python 支援多重繼承,一個子類可以有多個“直接父類”。這樣,就具備了“多個父類”的特點。但是由於,這樣會被“類的整體層次”搞的異常複雜,儘量避免使用。

#多重繼承

class A:

def aa(self):

print("aa")

class B:

def bb(self): print("bb")

class C(B,A):

def cc(self):

print("cc")

c = C()

c.cc()

c.bb()

c.aa()

 

運算結果: cc bb aa

MRO()

Python 支援多繼承,如果父類中有相同名字的方法,在子類沒有指定父類名時,直譯器將

“從左向右”按順序搜尋。

MRO(Method Resolution Order):方法解析順序。 我們可以通過 mro()方法獲得

“類的層次結構”,方法解析順序也是按照這個“類的層次結構”尋找的。

#多重繼承 class A:

def aa(self): print("aa")

def say(self):

print("say AAA!")

class B:

def bb(self): print("bb")

def say(self): print("say BBB!")

class C(B,A):

def cc(self):

print("cc")

c = C()

print(C.mro())                   #列印類的層次結構

c.say()    #直譯器尋找方法是“從左到右”的方式尋找,此時會執行B 類中的say()

super()獲得父類定義在子類中,如果想要獲得父類的方法時,我們可以通過 super()來做。 super()代表父類的定義,不是父類物件。

#super()

class A:

def say(self):

print("A: ",self) print("say AAA")

class B(A):

def say(self):

#A.say(self) 呼叫父類的say方法 super().say() #通過super()呼叫父類的方法

print("say BBB")

b = B()

b.say()

執行結果:

A:    <__main__.B object at 0x007A5690> say AAA say BBB 多型

多型(polymorphism)是指同一個方法呼叫由於物件不同可能會產生不同的行為。在現實生活中,我們有很多例子。比如:同樣是呼叫人的休息方法,張三的休息是睡覺,李四的休息是玩遊戲,高淇老師是敲程式碼。同樣是吃飯的方法,中國人用筷子吃飯,英國人用刀叉吃飯,印度人用手吃飯。

關於多型要注意以下 2 點:

  1. 多型是方法的多型,屬性沒有多型。
  2. 多型的存在有 2 個必要條件:繼承、方法重寫。

#多型

class Animal:

def shout(self):

print("動物叫了一聲")

class Dog(Animal):

def shout(self):

print("小狗,汪汪汪")

class Cat(Animal):

def shout(self):

print("小貓,喵喵喵")

def animalShout(a):

if isinstance(a,Animal):

a.shout() #傳入的物件不同,shout方法對應的實際行為也不同。

animalShout(Dog()) animalShout(Cat())

執行結果:小狗,汪汪汪小貓,喵喵喵

特殊方法和運算子過載

Python 的運算子實際上是通過呼叫物件的特殊方法實現的。比如:

a = 20 b = 30

c       = a+b

d      = a.__add__(b) print("c=",c) print("d=",d)

運算結果: c= 50 d= 50

常見的特殊方法統計如下:

方法

說明

例子

__init__

構造方法

物件建立:p = Person()

__del__

析構方法

物件回收

__repr__,__str__

列印,轉換

print(a)

__call__

函式呼叫

a()

__getattr__

點號運算

a.xxx

__setattr__

屬性賦值

a.xxx = value

__getitem__

索引運算

a[key]

__setitem__

索引賦值

a[key]=value

__len__

長度

len(a)

每個運算子實際上都對應了相應的方法,統計如下:

運算子

特殊方法

說明

運算子+

__add__

加法

運算子-

__sub__

減法

<,<=,==

__lt__,__le__,__eq__

比較運算子

>,>=,!=

__gt__,__ge__,__ne__

|,^,&

__or__,__xor__,__and__

或、異或、與

<<,>>

__lshift__,__rshift__

左移、右移

*,/,%,//

__mul__,__truediv__,__mod__,_

_floordiv__

乘、浮點除、模運算

(取餘)、整數除

**

__pow__

指數運算

我們可以重寫上面的特殊方法,即實現了“運算子的過載”。

#測試運算子的過載

class Person:

def __init__(self,name): self.name = name

def __add__(self, other):

if isinstance(other,Person):

return "{0}--{1}".format(self.name,other.name)

else:

return "不是同類物件,不能相加"

def __mul__(self, other):

if isinstance(other,int):

return self.name*other

else:

return "不是同類物件,不能相乘"

p1 = Person("高淇") p2 = Person("高希希")

x = p1 + p2

print(x) print(p1*3)

運算結果:

高淇--高希希高淇高淇高淇

特殊屬性

Python 物件中包含了很多雙下劃線開始和結束的屬性,這些是特殊屬性,有特殊用法。這裡我們列出常見的特殊屬性:

 

特殊方法

含義

 

 

obj.__dict__

物件的屬性字典

 

 

obj.__class__

物件所屬的類

 

 

class.__bases__

類的基類元組(多繼承)

 

 

class.__base__

類的基類

 

 

class.__mro__

類層次結構

 

 

class.__subclasses__()

子類列表

 

#測試特殊屬性 class A: pass

class B: pass class C(B,A):

def __init__(self,nn):

self.nn = nn

def cc(self):

print("cc")

c = C(3)

print(dir(c)) print(c.__dict__) print(c.__class__) print(C.__bases__) print(C.mro()) print(A.__subclasses__())

    

執行結果:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'cc', 'nn']

{'nn': 3}

<class '__main__.C'>

(<class '__main__.B'>, <class '__main__.A'>)

[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

[<class '__main__.C'>]

物件的淺拷貝和深拷貝

·變數的賦值操作只是形成兩個變數,實際還是指向同一個物件。 ·淺拷貝

Python 拷貝一般都是淺拷貝。拷貝時,物件包含的子物件內容不拷貝。因此,源物件和拷貝物件會引用同一個子物件。

·深拷貝使用 copy 模組的 deepcopy 函式,遞迴拷貝物件中包含的子物件。源物件和拷貝物件所有的子物件也不同。

#測試物件的引用賦值、淺拷貝、深拷貝 import copy

class MobilePhone: def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen

class CPU:

def calculate(self):

print("計算,算個 12345") print("CPU 物件:",self)

class Screen:

def show(self):

print("顯示一個好看的畫面,亮瞎你的鈦合金大眼")

print("螢幕物件:",self)

c = CPU() s = Screen() m = MobilePhone(c,s)

m.cpu.calculate()

n = m                               #兩個變數,但是指向了同一個物件

print(m,n)

m2 = copy.copy(m) #m2是新拷貝的另一個手機物件

print(m,m2)

m.cpu.calculate()

m2.cpu.calculate() #m2和m擁有了一樣的cpu物件和screen物件

m3 = copy.deepcopy(m)

m3.cpu.calculate()                   #m3和m擁有不一樣的cpu物件和screen

物件

運算結果:

計算,算個 12345

CPU 物件: <__main__.CPU object at 0x00685690>

<__main__.MobilePhone object at 0x00685B50> <__main__.MobilePhone object at 0x00685B50>

<__main__.MobilePhone object at 0x00685B50> <__main__.MobilePhone object at

0x0069B490>

計算,算個 12345

CPU 物件: <__main__.CPU object at 0x00685690> 計算,算個 12345

CPU 物件: <__main__.CPU object at 0x00685690> 計算,算個 12345

CPU 物件: <__main__.CPU object at 0x006A5DB0>

組合

“is-a”關係,我們可以使用“繼承”。從而實現子類擁有的父類的方法和屬性。“is-a” 關係指的是類似這樣的關係:狗是動物,dog is animal。狗類就應該繼承動物類。

“has-a”關係,我們可以使用“組合”,也能實現一個類擁有另一個類的方法和屬性。” has-a”關係指的是這樣的關係:手機擁有 CPU。 MobilePhone has a CPU。

#組合測試

class MobilePhone: def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen

class CPU:

def calculate(self):

print("計算,算個 12345")

class Screen:

def show(self):

print("顯示一個好看的畫面,亮瞎你的鈦合金大眼")

c = CPU() s = Screen() m = MobilePhone(c,s)

m.cpu.calculate()                #通過組合,我們也能呼叫cpu物件的方法。相

當於手機物件間接擁有了“cpu的方法”

m.screen.show()

運算結果:

計算,算個 12345

顯示一個好看的畫面,亮瞎你的鈦合金大眼設計模式_工廠模式實現

設計模式是面嚮物件語言特有的內容,是我們在面臨某一類問題時候固定的做法,設計模式有很多種,比較流行的是:GOF(Goup Of Four)23 種設計模式。當然,我們沒有必要全部學習,學習幾個常用的即可。

對於初學者,我們學習兩個最常用的模式:工廠模式和單例模式。

工廠模式實現了建立者和呼叫者的分離,使用專門的工廠類將選擇實現類、建立物件進行統一的管理和控制。

#工廠模式

class CarFactory:

def createCar(self,brand): if brand == "賓士": return Benz()

elif brand == "寶馬": return BMW()

elif brand == '比亞迪':

return BYD() else:

return "未知品牌,無法建立"

class Benz:

pass

class BMW: pass

class BYD:

pass

factory = CarFactory() c1 = factory.createCar("賓士") c2 = factory.createCar("寶馬") print(c1) print(c2)

執行結果:

<__main__.Benz object at 0x021C5770>

<__main__.BMW object at 0x021C5790> 設計模式_單例模式實現

單例模式(Singleton Pattern)的核心作用是確保一個類只有一個例項,並且提供一個訪問該例項的全域性訪問點。

單例模式只生成一個例項物件,減少了對系統資源的開銷。當一個物件的產生需要比較多的資源,如讀取配置檔案、產生其他依賴物件時,可以產生一個“單例物件”,然後永久駐留記憶體中,從而極大的降低開銷。

單例模式有多種實現的方式,我們這裡推薦重寫__new__()的方法。

#單例模式

class MySingleton:

__obj = None __init_flag = True

def __new__(cls, *args, **kwargs):

if cls.__obj == None: cls.__obj = object.__new__(cls) return cls.__obj

def __init__(self,name):

if MySingleton.__init_flag:

print("init....") self.name = name

MySingleton.__init_flag = False

a    = MySingleton("aa")

print(a)

b    = MySingleton("bb") print(b)

運算結果:

init....

<__main__.MySingleton object at 0x01E15610>

<__main__.MySingleton object at 0x01E15610>

設計模式稱之為“模式”,就是一些固定的套路。我們很容易用到其他場景上,比如前面講的工廠模式,我們需要將工廠類定義成“單例”,只需要簡單的套用即可實現:

#測試工廠模式和單例模式的整合使用 class CarFactory:

          __obj = None                   #類屬性

__init_flag = True

def create_car(self,brand): if brand =="賓士": return Benz()

elif brand =="寶馬": return BMW()

elif brand == "比亞迪":

return BYD()

else:

return "未知品牌,無法建立"

def __new__(cls, *args, **kwargs):

if cls.__obj ==None: cls.__obj = object.__new__(cls) return cls.__obj

def __init__(self):

if CarFactory.__init_flag:

print("init CarFactory....")

CarFactory.__init_flag = False

class Benz: pass

class BMW: pass

class BYD:

pass

factory = CarFactory() c1 = factory.create_car("賓士")

c2 = factory.create_car("比亞迪") print(c1) print(c2)

factory2 = CarFactory()

print(factory) print(factory2)

運算結果: init CarFactory....

<__main__.Benz object at 0x01E36E90> <__main__.BYD object at 0x01E36C30>

<__main__.CarFactory object at 0x01E36730>

<__main__.CarFactory object at 0x01E36730>

實操作業

  1. 如下程式碼測試物件的淺拷貝、深拷貝,請繪製出記憶體示意圖。

#測試物件的引用賦值、淺拷貝、深拷貝 import copy

class MobilePhone: def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen

class CPU:

def calculate(self):

print("計算,算個 12345") print("CPU 物件:",self)

class Screen:

def show(self):

print("顯示一個好看的畫面,亮瞎你的鈦合金大眼")

print("螢幕物件:",self)

c = CPU() s = Screen() m = MobilePhone(c,s)

m.cpu.calculate()

n = m                              #兩個變數,但是指向了同一個物件

print(m,n)

m2 = copy.copy(m) #m2是新拷貝的另一個手機物件 print(m,m2)

m.cpu.calculate()

m2.cpu.calculate() #m2和m擁有了一樣的cpu物件和screen物件

m3 = copy.deepcopy(m)

m3.cpu.calculate()      #m3和m擁有不一樣的cpu物件和screen 物件

  1. 定義發動機類 Motor、底盤類 Chassis、座椅類 Seat,車輛外殼類 Shell,並使用組合

關係定義汽車類。其他要求如下:定義汽車的 run()方法,裡面需要呼叫 Motor 類的 work()方法,也需要呼叫座椅

類 Seat 的 work()方法,也需要呼叫底盤類 Chassis 的 work()方法。

  1. 使用工廠模式、單例模式實現如下需求:

(1)    電腦工廠類 ComputerFactory 用於生產電腦 Computer。工廠類使用單例模式,也就是說只能有一個工廠物件。

(2)    工廠類中可以生產各種品牌的電腦:聯想、華碩、神舟

(3)    各種品牌的電腦使用繼承實現:

(4)    父類是 Computer 類,定義了 calculate 方法

(5)    各品牌電腦類需要重寫父類的 calculate 方法

  1. 定義一個 Employee 僱員類,要求如下:

(1)    屬性有:id、name、salary

(2)    運算子過載+:實現兩個物件相加時,預設返回他們的薪水和

(3)    構造方法要求:輸入 name、salary,不輸入 id。id 採用自增的方式,從 1000 開始自增,第一個新增物件是 1001,第二個新增物件是 1002。

(4)    根據 salary 屬性,使用@property 設定屬性的 get 和 set 方法。set 方法要求輸入:1000-50000 範圍的數字。

高淇Python400集視訊教程目錄

第一章 Python 入門

001. Python 介紹_特性_版本問題_應用範圍

002. Python 下載_安裝_配置_第一行 Python 程式

003. 開發環境介紹_互動模式的使用_IDLE 介紹和使用

004. IDLE 開發環境的使用_建立 Python 原始檔

005. Python 程式格式_縮排_行註釋_段註釋

006. 簡單錯誤如何處理_守破離學習法_程式設計師修煉手冊

007. 海龜繪圖_座標系問題_畫筆各種方法

008. 海龜繪圖_畫出奧運五環圖第二章 程式設計基本概念

  1. 程式的構成
  2. 物件的基本組成和記憶體示意圖
  3. 引用的本質_棧記憶體和堆記憶體_記憶體示意圖
  4. 識別符號_幫助系統的簡單使用_命名規則
  5. 變數的宣告_初始化_刪除變數_垃圾回收機制
  6. 鏈式賦值_系列解包賦值_常量
  7. 內建資料型別_基本算術運算子
  8. 整數_不同進位制_其他型別轉換成整數
  9. 浮點數_自動轉換_強制轉換_增強賦值運算子
  10. 時間表示_unix 時間點_毫秒和微秒_time 模組
  11. 多點座標_繪出折線圖_計算兩點距離
  12. 布林值_比較運算子_邏輯運算子_短路問題
  13. 同一運算子_整數快取問題
  14. 字串_unicode 字符集_三種建立字串方式_len()
  15. 字串_轉義字元_字串拼接_字串複製_input()獲得鍵盤輸入
  16. 字串_str()_使用[]提取字元_replace()替換生成新字串_記憶體分析
  17. 字串_切片 slice 操作_逆序
  18. 字串_split()分割_join()合併_join()效率測試
  19. 字串_駐留機制_記憶體分析_字串同一判斷_值相等判斷
  20. 字串_常用查詢方法_去除首位資訊_大小寫轉換_排版
  21. 字串_format 格式化_數字格式化操作
  22. 可變字串_io.StringIO
  23. 運算子總結_位操作符_優先順序問題第三章 序列
  24. 列表_特點_記憶體分析
  25. 建立列表的 4 種方式_推導式建立列表
  26. 列表_元素的 5 種新增方式_效率問題
  27. 列表_元素刪除的三種方式_刪除本質是陣列元素拷貝
  28. 列表_元素的訪問_元素出現次數統計_成員資格判斷
  29. 列表_切片 slice 操作
  30. 列表_排序_revered 逆序_max_min_sum
  31. 列表_二維列表_表格資料的儲存和讀取
  32. 元組_特點_建立的兩種方式_tuple()要點
  33. 元組_元素訪問_計數方法_切片操作_成員資格判斷_zip()
  34. 元組_生成器推導式建立元組_總結
  35. 字典_特點_4 種建立方式_普通_dict_zip_formkeys
  36. 字典_元素的訪問_鍵的訪問_值的訪問_鍵值對的訪問
  37. 字典_元素的新增_修改_刪除
  38. 字典_序列解包用於列表元組字典
  39. 字典_複雜表格資料儲存_列表和字典綜合巢狀
  40. 字典_核心底層原理_記憶體分析_儲存鍵值對過程
  41. 字典_核心底層原理_記憶體分析_查詢值物件過程
  42. 集合_特點_建立和刪除_交集並集差集運算

第四章 控制語句

  1. Pycharm 開發環境的下載安裝配置_專案建立和執行
  2. 單分支選擇結構_條件表示式詳解
  3. 雙分支選擇結構_三元運算子的使用詳解
  4. 多分支選擇結構
  5. 選擇結構的巢狀
  6. while 迴圈結構_死迴圈處理
  7. for 迴圈結構_遍歷各種可迭代物件_range 物件
  8. 巢狀迴圈
  9. 巢狀迴圈練習_九九乘法表_列印表格資料
  10. break 語句
  11. continue 語句
  12. else 語句
  13. 迴圈程式碼優化技巧(及其重要)
  14. zip()並行迭代
  15. 推導式建立序列_列表推導式_字典推導式_集合推導式_生成器推導式
  16. 綜合練習_繪製不同顏色的多個同心圓_繪製棋盤

第五章    函式

  1. 函式的基本概念_記憶體分析_函式的分類_定義和呼叫
  2. 形參和實參_文件字串_函式註釋
  3. 返回值詳解
  4. 函式也是物件_記憶體分析
  5. 變數的作用域_全域性變數_區域性變數_棧幀記憶體分析講解
  6. 區域性變數和全域性變數_效率測試
  7. 引數的傳遞_傳遞可變物件_記憶體分析
  8. 引數的傳遞_傳遞不可變物件_記憶體分析
  9. 淺拷貝和深拷貝_記憶體分析

10.引數的傳遞_不可變物件含可變子物件_記憶體分析

11.引數的型別_位置引數_預設值引數_命名引數

12.引數的型別_可變引數_強制命名引數

13.lambda 表示式和匿名函式

14.eval()函式用法

15.遞迴函式_函式呼叫記憶體分析_棧幀的建立

16.遞迴函式_階乘計算案例

17.巢狀函式_內部函式_資料隱藏

18.nonlocal_global

19. LEGB 規則

第六章 物件導向程式設計

  1. 物件導向和麵向過程的區別_執行者思維_設計者思維
  2. 物件的進化故事
  3. 類的定義_類和物件的關係
  4. 建構函式__init__
  5. 例項屬性_記憶體分析
  6. 例項方法_記憶體分析方法呼叫過程_dir()_isinstance
  7. 類物件
  8. 類屬性_記憶體分析建立類和物件的底層
  9. 類方法_靜態方法_記憶體分析圖示
  10. __del__()析構方法和垃圾回收機制
  11. __call__()方法和可呼叫物件
  12. 方法沒有過載_方法的動態性
  13. 私有屬性
  14. 私有方法
  15. @property 裝飾器_get 和 set 方法
  16. 物件導向的三大特徵說明(封裝、繼承、多型)
  17. 繼承
  18. 方法的重寫
  19. object 根類_dir()
  20. 重寫__str__()方法
  21. 多重繼承
  22. mro()
  23. super()獲得父類的定義
  24. 多型
  25. 特殊方法和運算子過載
  26. 特殊屬性
  27. 物件的淺拷貝和深拷貝_記憶體分析
  28. 組合
  29. 設計模式_工廠模式實現
  30. 設計模式_單例模式實現

第七章 模組

  1. 模組化程式設計理念_模組和包
  2. 標準庫有哪些
  3. 模組的設計和實現
  4. API 的設計
  5. 模組的匯入和使用
  6. 包的使用_匯入和建立
  7. 重新載入模組_動態代入模組
  8. 模組的匯入順序
  9. 名稱空間和名稱查詢順序
  10. 第三方擴充套件庫的介紹和安裝
  11. PyPy
  12. PiP 安裝第三方擴充套件庫

第八章 檔案操作(IO)

  1. 文字檔案和二進位制檔案
  2. 檔案操作相關模組介紹
  3. 建立檔案物件_文字檔案寫入
  4. with 語句_上下文管理
  5. 文字檔案的讀取
  6. 二進位制檔案的讀取和寫入
  7. 使用 pickle 序列化
  8. CSV 檔案的操作_csv 檔案讀取
  9. CSV 檔案的操作_csv 檔案寫入
  10. os 和 os.path 模組_檔案級操作
  11. os 和 os.path 模組_目錄級操作
  12. 儲存資料_json.dump()和 json.load()
  13. 練習 1
  14. 練習 2
  15. 練習 3

第九章 異常和錯誤

  1. 異常處理結構_try_except
  2. try_finally
  3. raise 丟擲異常
  4. 自定義異常
  5. 常見異常的解決
  6. with 上下文管理器
  7. 程式除錯
  8. 使用 pdb 模組除錯程式
  9. IDLE 的除錯 10. Pycharm 開發環境的除錯

第十章    數值日期和複雜文字處理

  1. math 模組
  2. random 模組_隨機整數_隨機序列
  3. 數值運算模組 NumPy_陣列
  4. 數值運算模組 NumPy_應用
  5. 時間表示原理_時間物件
  6. 日期物件 7. 字串和日期物件互相轉換

第 11 章 GUI 程式設計

  1. Tkinter 介紹
  2. 第一個 GUI 程式
  3. 事件處理
  4. 控制元件_Button 按鈕
  5. 控制元件_Canvas 畫布
  6. 控制元件_Chekbutton_Radiobutton
  7. 控制元件_Entry 文字輸入框
  8. 控制元件_Frame 容器
  9. 控制元件_Label_Message
  10. 控制元件_選單製作_Menu_Menubutton
  11. 對話方塊_通用訊息對話方塊
  12. 對話方塊_檔案對話方塊
  13. 對話方塊_顏色選擇對話方塊
  14. 幾何管理器_網路管理器
  15. 幾何管理器_包管理器
  16. 幾何管理器_位置管理器
  17. 鍵盤事件處理
  18. 滑鼠事件處理
  19. 動畫的實現
  20. 【專案】_房貸計算器_1
  21. 【專案】_房貸計算器_2
  22. 【專案】_房貸計算器_3
  23. 【專案】_房貸計算器_4
  24. 【專案】_房貸計算器_4

第 12 章 【專案】坦克大戰遊戲開發

  1. pygame 使用介紹_簡單遊戲程式設計
  2. pygame 事件處理_圖形影像處理
  3. pygame 精靈_碰撞檢測
  4. 坦克大戰物件導向分析
  5. 坦克大戰之載入主頁面
  6. 坦克大戰之-新增左上角提示文字
  7. 坦克大戰之新增事件監聽
  8. 坦克大戰之載入我方坦克_
  9. 坦克大戰之實現我方坦克的移動
  10. 坦克大戰之優化坦克移動方式
  11. 坦克大戰之新增隨機生成敵方坦克
  12. 坦克大戰之實現敵方坦克的隨機移動
  13. 坦克大戰之我方坦克發射子彈
  14. 坦克大戰之子彈的優化處理
  15. 新增敵方坦克隨機發射子彈?
  16. 敵方子彈與我方法坦克的碰撞以及我方坦克重生
  17. 坦克大戰之敵方坦克的碰撞檢測
  18. 坦克大戰之爆炸效果的實現
  19. 爆炸效果補充

20 坦克大戰之音效處理第 13 章 圖形繪製

  1. 基於 turtle 的畫筆繪圖_矩形_多邊形_圓形
  2. 基於 turtle 的畫筆繪圖_遞迴圖形
  3. 基於 turtle 的畫筆繪圖_實操練習
  4. 基於 tkinter 圖形繪製_矩形_線條_字串
  5. 基於 tkinter 圖形繪製_繪製函式圖形
  6. Matplotlib_安裝
  7. Matplotlib_簡單圖形繪製
  8. Matplotlib_函式曲線
  9. Matplotlib_多個圖形
  10. Matplotlib_繪製直方圖第 14 章 資料庫程式設計
  11. 內建資料庫 SQLite
  12. 操作 SQLite 資料庫_Connection_Cursor_Row
  13. Mysql 資料庫的安裝和配置
  14. Mysql 視覺化客戶端軟體安裝
  15. PyMysql 的使用
  16. 對於資料庫表的增刪改查操作 1
  17. 對於資料庫表的增刪改查操作 2
  18. 對於資料庫表的增刪改查操作 3

第 15 章 多執行緒和併發程式設計

1:簡述多程式

2:程式類

3:多程式預設不共享資料

4:子類建立

5:程式池

6:程式間通訊 1

7:程式間通訊 2

8:簡述多執行緒

9:建立子執行緒

10:執行多執行緒程式

11:執行緒同步

12:同步非同步

13:生產者消費者模式 14:threadlocal 變數第 16 章 網路程式設計

1:網路基礎

2:埠號的作用

3:協議

4:socket 程式設計

5:使用 udp 協議傳送資料

6:使用 udp 協議接收資料

7:tftp 協議

8:tftp 下載原理

9:tftp 下載器

10:udp 廣播

11:tcp 通訊過程

12:tcp 伺服器

13:多程式伺服器

14. 聊天室專案_1

15_聊天室專案_2

16_聊天室專案_3

  1. 聊天室專案_4
  2. 聊天室專案_5

第 17 章 協程和非同步 IO

  1. 協程的概念
  2. 定義一個協程
  3. 建立 task 執行協程_繫結回撥
  4. future 與 result
  5. 阻塞和 await
  6. 併發和並行
  7. 協程巢狀
  8. 協程停止
  9. master-worker 主從模式
  10. aiohttp

第 18 章 指令碼化系統管理

  1. 增強的互動式環境 IPython_IPython 安裝_配置_啟動
  2. 和 IPython 簡單互動
  3. magic 函式
  4. 執行外部系統命令和執行檔案
  5. 檔案管理
  6. 定時執行任務

第19章 語音識別模組_中英文實時翻譯專案

  1. 語音識別基本原理
  2. 文字轉換語音
  3. 呼叫訊飛動態連結庫實現語音識別
  4. 百度語音 API 呼叫
  5. 【專案】實現中英文實時口語翻譯_1
  6. 【專案】實現中英文實時口語翻譯_2
  7. 【專案】實現中英文實時口語翻譯_3
  8. 【專案】實現中英文實時口語翻譯_4
  9. 【專案】實現中英文實時口語翻譯_5
  10. 【專案】實現中英文實時口語翻譯_6

第 20 章 Windows 下的 Python 開發

  1. Python3 安裝 pywin32 模組
  2. Windows 程式設計基本知識_元件物件
  3. 實現 COM 元件
  4. 對 word 的自動訪問
  5. 對 excel 的自動訪問
  6. 操作視窗
  7. 操作程式
  8. 操作執行緒
  9. 桌面便籤軟體開發_1
  10. 桌面便籤軟體開發_2
  11. 桌面便籤軟體開發_3
  12. 桌面便籤軟體開發_4
  13. 桌面便籤軟體開發_5

第 21 章 【實戰】演算法

  1. 演算法概述
  2. 時間複雜度分析
  3. 增長量級的概念
  4. 空間複雜度分析
  5. 查詢演算法_順序查詢法
  6. 查詢演算法_二分查詢法
  7. 排序演算法_氣泡排序法
  8. 排序演算法_選擇排序法
  9. 排序演算法_插入排序法
  10. 排序演算法_歸併排序法
  11. 排序演算法_快速排序法
  12. Python 提供的查詢演算法
  13. Python 提供的排序演算法

第 22 章 【實戰】手寫資料結構

  1. 自定義佇列_佇列特點_入隊
  2. 自定義佇列_出隊_測試
  3. 自定義棧_入棧
  4. 自定義棧_出棧
  5. 自定義二叉樹_分析和建立類
  6. 自定義二叉樹_建立左子樹_建立右子樹
  7. 自定義二叉樹_前序遍歷_後序遍歷_中序遍歷
  8. 自定義二叉樹_測試
  9. 自定義有向圖_特點分析

10.自定義有向圖_建立

  1. 自定義有向圖_路徑搜尋功能
  2. 自定義集合_類的建立和分析
  3. 自定義集合_新增元素_刪除元素
  4. 自定義集合_差集運算
  5. 自定義集合_交集運算
  6. 自定義集合_並集運算
  7. 自定義集合_運算子過載_集合包含關係處理
  8. 自定義集合_運算子過載_支援 in 運算子
  9. 手寫資料結構總結

第 23 章 正規表示式和複雜文字操作

1:正規表示式介紹_特殊符號和字元

2:字元匹配

3:建立字符集_表示字符集的特殊字元

4:限定範圍和否定

5:使用小括號指定分組

6:貪婪模式和非貪婪模式

7:常用正規表示式示例

8:re 模組_核心函式和方法_compile()方法

9:匹配物件和 group()以及 groups()方法

10:match()匹配字串

11:search()在一個字串中查詢 12:案例:程式設計實現抓取 163 主頁所有圖片地址資訊

第 24 章 函數語言程式設計和高階函式

  1. 函數語言程式設計是什麼
  2. 高階函式是什麼
  3. 將函式作為引數
  4. 返回值為函式
  5. map()高階函式_1 6. map()高階函式_2
    1. map()高階函式_3
    2. reduce()高階函式_1
    3. reduce()高階函式_2
    4. reduce()高階函式_3
    5. filter()高階函式
    6. sorted()高階函式
    7. 閉包_1 14. 閉包_2
      1. 閉包_3
      2. 匿名函式
      3. 裝飾器_1 18. 裝飾器_2
      4. 裝飾器_3
      5. 偏函式

第 25 章 測試

  1. 單元測試_unittest
  2. 單元測試_coverage
  3. 效能測試_timeit
  4. 效能測試_profile
  5. 效能測試_line profiler
  6. 效能測試_memory profiler
  7. 效能測試_pympler

第 26 章 【專案】手工實現神經網路

  1. 神經網路基本原理_1
  2. 神經網路基本原理_2
  3. MNIST 資料集簡介
  4. 矩陣原理_1
  5. 矩陣原理_相關運算_2
  6. 正態分佈原理_1
  7. 正態分佈原理_2
  8. Sigmoid 函式_神經網路啟用函式_1
  9. Sigmoid 函式_神經網路啟用函式_2
  10. 使用 matplotlib 繪製 Sigmoid 函式
  11. 前向反饋_feedforward 函式_1
  12. 前向反饋_feedforward 函式_2
  13. 前向反饋_feedforward 函式_3
  14. 神經網路學習能力構建_隨機梯度下降函式_1
  15. 神經網路學習能力構建_隨機梯度下降函式_2

16.神經網路學習能力構建_隨機梯度下降函式_3

17.神經網路學習能力構建_隨機梯度下降函式_4

18.神經網路學習能力構建_隨機梯度下降函式_5

19.MNIST 資料集的載入

20.測試和訓練神經網路

轉載於:https://www.cnblogs.com/Dar-/p/9721266.html