看完這篇文章,你的Python基礎就差很少了

傳送門:看完這篇文章,你的Linux基礎就差很少了

前言

本文是基於黑馬程序員2018年的Python基礎班的內容編寫的,以2018年的資料爲藍本,2016年的資料爲補充,還參考了一些網上的教程,旨在更好地總結Python基礎知識,力求簡明扼要,以供實戰演練時可以快速查詢遺忘的知識點。全文分爲兩大部分,分別是Python基礎語法和麪向對象。html

第一部分 Python基礎語法

第一部分 Python基礎語法

1. 認識Python

1.1 Python 簡介

Python 的創始人爲吉多·範羅蘇姆(Guido van Rossum)。python

Python 的設計目標:git

  • 一門簡單直觀的語言並與主要競爭者同樣強大
  • 開源,以便任何人均可覺得它作貢獻
  • 代碼像純英語那樣容易理解
  • 適用於短時間開發的平常任務

Python 的設計哲學:程序員

  • 優雅、明確、簡單

Python 開發者的哲學是:用一種方法,最好是隻有一種方法來作一件事面試

Python 是徹底面向對象的語言,在 Python 中一切皆對象。算法

可擴展性:若是須要一段關鍵代碼運行得更快或者但願某些算法不公開,能夠把這部分程序用 CC++ 編寫,而後在 Python 程序中使用它們。shell

1.2. 第一個Python程序

執行 Python 程序的三種方式: 解釋器、交互式運行、IDE運行編程

Python 是一個格式很是嚴格的程序設計語言。 python 2.x 默認不支持中文ubuntu

  • ASCII 字符只包含 256 個字符,不支持中文
  • Python 2.x 的解釋器名稱是 python
  • Python 3.x 的解釋器名稱是 python3

爲了照顧現有的程序,官方提供了一個過渡版本 —— Python 2.6設計模式

提示:若是開發時,沒法當即使用 Python 3.0(還有極少的第三方庫不支持 3.0 的語法),建議

  • 先使用 Python 3.0 版本進行開發
  • 而後使用 Python 2.6Python 2.7 來執行,而且作一些兼容性的處理

IPython 是一個 python 的 交互式 shell,比默認的 python shell 好用得多,它支持 bash shell 命令,適合於學習/驗證 Python 語法或者局部代碼。

集成開發環境(IDE,Integrated Development Environment)—— 集成了開發軟件須要的全部工具,通常包括如下工具:

  • 圖形用戶界面
  • 代碼編輯器(支持 代碼補全自動縮進
  • 編譯器/解釋器
  • 調試器(斷點單步執行
  • ……

PyCharm 是 Python 的一款很是優秀的集成開發環境

PyCharm運行工具欄

1.3. PyCharm 的設置

PyCharm 的 配置信息 是保存在 用戶家目錄下.PyCharmxxxx.x 目錄下的,xxxx.x 表示當前使用的 PyCharm 的版本號

1.3.1 恢復 PyCharm 的初始設置:
    1. 關閉正在運行的 PyCharm
    1. 在終端中執行如下終端命令,刪除 PyCharm 的配置信息目錄:
$ rm -r ~/.PyCharm2016.3
複製代碼
    1. 從新啓動 PyCharm
1.3.2 PyCharm 安裝和啓動步驟:
    1. 執行如下終端命令,解壓縮下載後的安裝包
$ tar -zxvf pycharm-professional-2017.1.3.tar.gz
複製代碼
    1. 將解壓縮後的目錄移動到 /opt 目錄下,能夠方便其餘用戶使用

/opt 目錄用戶存放給主機額外安裝的軟件

$ sudo mv pycharm-2017.1.3/ /opt/
複製代碼
    1. 切換工做目錄
$ cd /opt/pycharm-2017.1.3/bin
複製代碼
    1. 啓動 PyCharm
$ ./pycharm.sh
複製代碼
1.3.3 設置啓動圖標
  • 在專業版中,選擇菜單 Tools / Create Desktop Entry... 能夠設置任務欄啓動圖標
  • 注意:設置圖標時,須要勾選 Create the entry for all users 快捷方式文件 /usr/share/applications/jetbrains-pycharm.desktop

ubuntu 中,應用程序啓動的快捷方式一般都保存在 /usr/share/applications 目錄下

1.3.4 卸載以前版本的 PyCharm

要卸載 PyCharm 只須要作如下兩步工做:

    1. 刪除解壓縮目錄
$ sudo rm -r /opt/pycharm-2016.3.1/
複製代碼
    1. 刪除家目錄下用於保存配置信息的隱藏目錄
$ rm -r ~/.PyCharm2016.3/
複製代碼

若是再也不使用 PyCharm 還須要將 /usr/share/applications/ 下的 jetbrains-pycharm.desktop 刪掉

1.4. 多文件項目的演練

  • 開發 項目 就是開發一個 專門解決一個複雜業務功能的軟件
  • 一般每 一個項目 就具備一個 獨立專屬的目錄,用於保存 全部和項目相關的文件
  • PyCharm 中,要想讓哪個 Python 程序可以執行,必須首先經過 鼠標右鍵的方式執行 一下
  • 對於初學者而言,在一個項目中設置多個程序能夠執行,是很是方便的,能夠方便對不一樣知識點的練習和測試
  • 對於商業項目而言,一般在一個項目中,只有一個 能夠直接執行的 Python 源程序

讓選中的程序能夠執行

2. 註釋

  • 註釋的做用 使用用本身熟悉的語言,在程序中對某些代碼進行標註說明,加強程序的可讀性

2.1 單行註釋(行註釋)

  • # 開頭,# 右邊的全部東西都被當作說明文字,而不是真正要執行的程序,只起到輔助說明做用
print("hello python")  # 輸出 `hello python`
複製代碼

爲了保證代碼的可讀性,# 後面建議先添加一個空格,而後再編寫相應的說明文字;爲了保證代碼的可讀性,註釋和代碼之間 至少要有 兩個空格。

2.2 多行註釋(塊註釋)

  • 要在 Python 程序中使用多行註釋,能夠用 一對 連續的 三個 引號(單引號和雙引號均可以)
""" 這是一個多行註釋 在多行註釋之間,能夠寫不少不少的內容…… """ 
print("hello python")
複製代碼

提示:

  1. 註釋不是越多越好,對於一目瞭然的代碼,不須要添加註釋
  2. 對於 複雜的操做,應該在操做開始前寫上若干行註釋
  3. 對於 不是一目瞭然的代碼,應在其行尾添加註釋(爲了提升可讀性,註釋應該至少離開代碼 2 個空格)
  4. 毫不要描述代碼,假設閱讀代碼的人比你更懂 Python,他只是不知道你的代碼要作什麼

2.3 代碼規範:

3. 運算符

3.1 算數運算符

是完成基本的算術運算使用的符號,用來處理四則運算,而「+」和「*」還能夠用來處理字符串。

運算符 描述 實例
+ 10 + 20 = 30
- 10 - 20 = -10
* 10 * 20 = 200
/ 10 / 20 = 0.5
// 取整除 返回除法的整數部分(商) 9 // 2 輸出結果 4
% 取餘數 返回除法的餘數 9 % 2 = 1
** 又稱次方、乘方,2 ** 3 = 8

3.2 比較(關係)運算符

運算符 描述
== 檢查兩個操做數的值是否 相等,若是是,則條件成立,返回 True
!= 檢查兩個操做數的值是否 不相等,若是是,則條件成立,返回 True
> 檢查左操做數的值是否 大於 右操做數的值,若是是,則條件成立,返回 True
< 檢查左操做數的值是否 小於 右操做數的值,若是是,則條件成立,返回 True
>= 檢查左操做數的值是否 大於或等於 右操做數的值,若是是,則條件成立,返回 True
<= 檢查左操做數的值是否 小於或等於 右操做數的值,若是是,則條件成立,返回 True
  • Python 2.x 中判斷 不等於 還可使用 <> 運算符
  • != 在 Python 2.x 中一樣能夠用來判斷 不等於

3.3 賦值運算符

  • 在 Python 中,使用 = 能夠給變量賦值。在算術運算時,爲了簡化代碼的編寫,Python 還提供了一系列的 與 算術運算符 對應的 賦值運算符,注意:賦值運算符中間不能使用空格
運算符 描述 實例
= 簡單的賦值運算符 c = a + b 將 a + b 的運算結果賦值爲 c
+= 加法賦值運算符 c += a 等效於 c = c + a
-= 減法賦值運算符 c -= a 等效於 c = c - a
*= 乘法賦值運算符 c *= a 等效於 c = c * a
/= 除法賦值運算符 c /= a 等效於 c = c / a
//= 取整除賦值運算符 c //= a 等效於 c = c // a
%= (餘數)賦值運算符 c %= a 等效於 c = c % a
**= 冪賦值運算符 c **= a 等效於 c = c ** a

3.4 身份運算符

身份運算符比較兩個對象的內存位置。經常使用的有兩個身份運算符,以下所述:

運算符 描述 示例
is 判斷兩個標識符是否是引用同一個對象 x is y,相似 id(x) == id(y)
is not 判斷兩個標識符是否是引用不一樣對象 x is not y,相似 id(a) != id(b)

辨析

  • is 用於判斷 兩個變量引用的對象是否爲同一個
  • == 用於判斷 引用變量的 是否相等

3.5 成員運算符

Python成員運算符測試給定值是否爲序列中的成員。 有兩個成員運算符,以下所述:

運算符 描述
in 若是在指定的序列中找到一個變量的值,則返回true,不然返回false。
not in 若是在指定序列中找不到變量的值,則返回true,不然返回false。

3.6 邏輯運算符

運算符 邏輯表達式 描述
and x and y 只有 x 和 y 的值都爲 True,纔會返回 True
不然只要 x 或者 y 有一個值爲 False,就返回 False
or x or y 只要 x 或者 y 有一個值爲 True,就返回 True
只有 x 和 y 的值都爲 False,纔會返回 False
not not x 若是 x 爲 True,返回 False
若是 x 爲 False,返回 True

3.7 運算符優先級

  • 如下表格的算數優先級由高到最低順序排列:
運算符 描述
** 冪 (最高優先級)
* / % // 乘、除、取餘數、取整除
+ - 加法、減法
<= < > >= 比較運算符
== != 等於運算符
= %= /= //= -= += *= **= 賦值運算符
is is not 身份運算符
in not in 成員運算符
not or and 邏輯運算符

<補>程序執行原理

Python程序執行示意圖

  1. 操做系統會首先讓 CPUPython 解釋器 的程序複製到 內存
  2. Python 解釋器 根據語法規則,從上向下CPU 翻譯 Python 程序中的代碼
  3. CPU 負責執行翻譯完成的代碼

Python 的解釋器有多大?

  • 執行如下終端命令能夠查看 Python 解釋器的大小
# 1. 確認解釋器所在位置
$ which python

# 2. 查看 python 文件大小(只是一個軟連接)
$ ls -lh /usr/bin/python

# 3. 查看具體文件大小
$ ls -lh /usr/bin/python2.7
複製代碼

4. 變量

4.1 變量定義

  • 在 Python 中,每一個變量 在使用前都必須賦值,變量 賦值之後 該變量 纔會被建立
  • 能夠用 其餘變量的計算結果 來定義變量
  • 變量名 只有在 第一次出現 纔是 定義變量
變量名 = 值
複製代碼

使用交互式方式,若是要查看變量內容,直接輸入變量名便可,不須要使用 print 函數 使用解釋器執行,若是要輸出變量的內容,必需要要使用 print 函數

4.2 變量的類型

  • Python 中定義變量是 不須要指定類型(在其餘不少高級語言中都須要),Python 能夠根據 = 等號右側的值,自動推導出變量中存儲數據的類型
  • 數據類型能夠分爲 數字型非數字型
    • 數字型
      • 整型 (int):Python3中的全部整數都表示爲長整數。 所以,長整數沒有單獨的數字類型。
      • 浮點型(float
      • 布爾型(bool) :真 True 非 0 數 —— 非零即真,假 False 0
      • 複數型 (complex):複數是由x + yj表示的有序對的實數浮點數組成,其中x和y是實數,j是虛數單位。
    • 非數字型:有些運算符還支持這些數據類型,詳見4.4.5.3 運算符。
      • 字符串(str):加號(+)是字符串鏈接運算符,星號(*)是重複運算符。
      • 列表(list
      • 元組(tuple
      • 字典(dict

提示:在 Python 2.x 中,整數 根據保存數值的長度還分爲:

  • int(整數)
  • long(長整數)
  • 使用 type 函數能夠查看一個變量的類型
In [1]: type(name)
複製代碼
<補>不一樣類型變量之間的計算
  1. 數字型變量 之間能夠直接計算
  • 在 Python 中,兩個數字型變量是能夠直接進行 算數運算的
  • 若是變量是 bool 型,在計算時
    • True 對應的數字是 1
    • False 對應的數字是 0
  1. 字符串變量 之間使用 + 拼接字符串
  2. 字符串變量 能夠和 整數 使用 * 重複拼接相同的字符串
  3. 數字型變量字符串 之間 不能進行其餘計算
<補>從鍵盤獲取輸入信息:input
  • 在 Python 中可使用 input 函數從鍵盤等待用戶的輸入
  • 用戶輸入的 任何內容 Python 都認爲是一個 字符串
字符串變量 = input("提示信息:")
複製代碼
<補>類型轉換函數
函數 說明
int(x) 將 x 轉換爲一個整數
float(x) 將 x 轉換到一個浮點數
str(x) 將對象x轉換爲字符串表示形式
tuple(s) 將s轉換爲元組
list(s) 將s轉換爲列表
price = float(input("請輸入價格:"))
複製代碼
<補>格式化輸出:print
  • 若是但願輸出文字信息的同時,一塊兒輸出 數據,就須要使用到 格式化操做符
  • % 被稱爲 格式化操做符,專門用於處理字符串中的格式
    • 包含 % 的字符串,被稱爲 格式化字符串
    • % 和不一樣的 字符 連用,不一樣類型的數據 須要使用 不一樣的格式化字符
格式化字符 含義
%s 字符串
%d 有符號十進制整數,%06d 表示輸出的整數顯示位數,不足的地方使用 0 補全
%f 浮點數,%.2f 表示小數點後只顯示兩位
%% 輸出 %
  • 語法格式以下:
print("格式化字符串" % 變量1)

print("格式化字符串" % (變量1, 變量2...))
複製代碼

4.3 變量的命名

4.3.1 標識符和關鍵字

標示符就是程序員定義的 變量名函數名

  • 標示符能夠由 字母下劃線數字 組成
  • 不能以數字開頭
  • 不能與關鍵字重名

關鍵字 就是在 Python 內部已經使用的標識符

  • 關鍵字 具備特殊的功能和含義
  • 開發者 不容許定義和關鍵字相同的名字的標識符
  • 經過如下命令能夠查看 Python 中的關鍵字
In [1]: import keyword
In [2]: print(keyword.kwlist)
複製代碼
4.3.2 變量的命名規則

命名規則 能夠被視爲一種 慣例,並沒有絕對與強制 目的是爲了 增長代碼的識別和可讀性 注意 Python 中的 標識符區分大小寫的

  1. 在定義變量時,爲了保證代碼格式,= 的左右應該各保留一個空格
  2. 在 Python 中,若是 變量名 須要由 二個 或 多個單詞 組成時,能夠按照如下方式命名:每一個單詞都使用小寫字母,單詞與單詞之間使用 _下劃線 鏈接,例如:first_namelast_nameqq_numberqq_password

固然,還有駝峯命名法: 小駝峯式命名法:第一個單詞以小寫字母開始,後續單詞的首字母大寫,例如:firstNamelastName大駝峯式命名法,每個單詞的首字母都採用大寫字母,例如:FirstNameLastNameCamelCase

4.4 高級變量類型

Python 中,全部 非數字型變量 都支持如下特色: 1. 都是一個 序列 sequence,也能夠理解爲 容器 2. 取值 [] 3. 遍歷 for in 4. 計算長度len最大/最小值max/min比較刪除del 5. 連接 +重複 * 6. 切片

4.4.1 列表(list)

  • List(列表) 是 Python 中使用 最頻繁 的數據類型,在其餘語言中一般叫作 數組,專門用於存儲 一串 信息,列表用 [] 定義,數據 之間使用 , 分隔,列表的 索引0 開始。

索引 就是數據在 列表 中的位置編號,索引 又能夠被稱爲 下標 注意:從列表中取值時,若是 超出索引範圍,程序會報錯

name_list = ["zhangsan", "lisi", "wangwu"]
複製代碼

列表示意圖

<補>del 關鍵字
  • 使用 del 關鍵字(delete) 一樣能夠刪除列表中元素
  • del 關鍵字本質上是用來 將一個變量從內存中刪除的
  • 若是使用 del 關鍵字將變量從內存中刪除,後續的代碼就不能再使用這個變量了
In [1]: l = [1,2,3,4]
In [2]: del l[1]
In [3]: l[1]
Out[3]: 3
複製代碼

在平常開發中,要從列表刪除數據,建議 使用列表提供的方法

<補>函數與方法
  • 函數 封裝了獨立功能,能夠直接調用
函數名(參數)
複製代碼

函數須要死記硬背

  • 方法 和函數相似,一樣是封裝了獨立的功能
  • 方法 須要經過 對象 來調用,表示針對這個 對象 要作的操做
對象.方法名(參數)
複製代碼

在變量後面輸入 .,而後選擇針對這個變量要執行的操做,記憶起來比函數要簡單不少

<補>循環遍歷
  • 遍歷 就是 從頭至尾 依次列表 中獲取數據,在 循環體內部 針對 每個元素,執行相同的操做。

  • 在 Python 中爲了提升列表的遍歷效率,使用 for 就可以實現迭代遍歷。

# for 循環內部使用的變量 in 列表
for name in name_list:
    循環內部針對列表元素進行操做
    print(name)
複製代碼

for-in循環流程圖

  • 儘管 Python 的 列表 中能夠 存儲不一樣類型的數據
  • 可是在開發中,更多的應用場景是
    1. 列表 存儲相同類型的數據
    2. 經過 迭代遍歷,在循環體內部,針對列表中的每一項元素,執行相同的操做

4.4.2 元組(tuple)

  • Tuple(元組)與列表相似,不一樣之處在於元組的 元素不能修改
    • 元組 表示多個元素組成的序列
    • 元組Python 開發中,有特定的應用場景
      • 用於存儲 一串 信息數據 之間使用 , 分隔
    • 元組用 () 定義,元組的 索引0 開始,索引 就是數據在 元組 中的位置編號。
info_tuple = ("zhangsan", 18, 1.75)
複製代碼

建立空元組:

info_tuple = ()
複製代碼

元組中 只包含一個元素 時,須要 在元素後面添加逗號

info_tuple = (50, )
複製代碼

元組示意圖

  • 在 Python 中,可使用 for 循環遍歷全部非數字型類型的變量:列表元組字典 以及 字符串
  • 提示:在實際開發中,除非 可以確認元組中的數據類型,不然針對元組的循環遍歷需求並非不少
  • 在開發中,更多的應用場景是:
    • 函數的 參數 和 返回值,一個函數能夠接收 任意多個參數,或者 一次返回多個數據
    • 格式字符串,格式化字符串後面的 () 本質上就是一個元組
    • 讓列表不能夠被修改,以保護數據安全
<補>元組和列表之間的轉換
  • 使用 list 函數能夠把元組轉換成列表
list(元組) 
複製代碼
  • 使用 tuple 函數能夠把列表轉換成元組
tuple(列表)
複製代碼

4.4.3 字典(dict)

dict(字典) 是 除列表之外 Python 之中 最靈活 的數據類型。 字典一樣能夠用來 存儲多個數據,一般用於存儲 描述一個 物體 的相關信息

  • 和列表的區別:
    • 列表有序 的對象集合
    • 字典無序 的對象集合
  • 字典用 {} 定義。
  • 字典使用 鍵值對 存儲數據,鍵值對之間使用逗號 , 分隔:
    • key 是索引
    • value 是數據
    • 之間使用冒號 : 分隔
    • 鍵必須是惟一的
    • 能夠取任何數據類型,但 只能使用 字符串數字元組
xiaoming = {"name": "小明",
            "age": 18,
            "gender": True,
            "height": 1.75}
複製代碼

字典示意圖

  • 字典的遍歷 就是 依次 從 字典 中獲取全部鍵值對:
# for 循環內部使用的 `key 的變量` in 字典
for k in xiaoming:

    print("%s: %s" % (k, xiaoming[k]))
複製代碼

提示:在實際開發中,因爲字典中每個鍵值對保存數據的類型是不一樣的,因此針對字典的循環遍歷需求並非不少

  • 儘管可使用 for in 遍歷 字典
  • 可是在開發中,更多的應用場景是:
    • 使用 多個鍵值對,存儲 描述一個 物體 的相關信息 —— 描述更復雜的數據信息
    • 多個字典 放在 一個列表 中,再進行遍歷,在循環體內部針對每個字典進行 相同的處理
card_list = [{"name": "張三",
              "qq": "12345",
              "phone": "110"},
             {"name": "李四",
              "qq": "54321",
              "phone": "10086"}
             ]

複製代碼

4.4.4 字符串(str)

  • 字符串 就是 一串字符,是編程語言中表示文本的數據類型
  • 在 Python 中可使用 一對雙引號 " 或者 一對單引號 ' 定義一個字符串
    • 雖然可使用 \" 或者 \' 作字符串的轉義,可是在實際開發中:
      • 若是字符串內部須要使用 ",可使用 ' 定義字符串
      • 若是字符串內部須要使用 ',可使用 " 定義字符串
  • 可使用 索引 獲取一個字符串中 指定位置的字符,索引計數從 0 開始
  • 也可使用 for 循環遍歷 字符串中每個字符

大多數編程語言都是用 " 來定義字符串

string = "Hello Python"

for c in string:
    print(c)

複製代碼

字符串示意圖

提示:在 python 中對字符串操做,內置提供的方法足夠多,使得在開發時,可以針對字符串進行更加靈活的操做!應對更多的開發需求!

1) 判斷類型 - 9
方法 說明
string.isspace() 若是 string 中只包含空格,則返回 True
string.isalnum() 若是 string 至少有一個字符而且全部字符都是字母或數字則返回 True
string.isalpha() 若是 string 至少有一個字符而且全部字符都是字母則返回 True
string.isdecimal() 若是 string 只包含數字則返回 True,全角數字
string.isdigit() 若是 string 只包含數字則返回 True,全角數字\u00b2
string.isnumeric() 若是 string 只包含數字則返回 True,全角數字漢字數字
string.istitle() 若是 string 是標題化的(每一個單詞的首字母大寫)則返回 True
string.islower() 若是 string 中包含至少一個區分大小寫的字符,而且全部這些(區分大小寫的)字符都是小寫,則返回 True
string.isupper() 若是 string 中包含至少一個區分大小寫的字符,而且全部這些(區分大小寫的)字符都是大寫,則返回 True
2) 查找和替換 - 7
方法 說明
string.startswith(str) 檢查字符串是不是以 str 開頭,是則返回 True
string.endswith(str) 檢查字符串是不是以 str 結束,是則返回 True
string.find(str, start=0, end=len(string)) 檢測 str 是否包含在 string 中,若是 start 和 end 指定範圍,則檢查是否包含在指定範圍內,若是是返回開始的索引值,不然返回 -1
string.rfind(str, start=0, end=len(string)) 相似於 find(),不過是從右邊開始查找
string.index(str, start=0, end=len(string)) 跟 find() 方法相似,不過若是 str 不在 string 會報錯
string.rindex(str, start=0, end=len(string)) 相似於 index(),不過是從右邊開始
string.replace(old_str, new_str, num=string.count(old)) 把 string 中的 old_str 替換成 new_str,若是 num 指定,則替換不超過 num 次
3) 大小寫轉換 - 5
方法 說明
string.capitalize() 把字符串的第一個字符大寫
string.title() 把字符串的每一個單詞首字母大寫
string.lower() 轉換 string 中全部大寫字符爲小寫
string.upper() 轉換 string 中的小寫字母爲大寫
string.swapcase() 翻轉 string 中的大小寫
4) 文本對齊 - 3
方法 說明
string.ljust(width) 返回一個原字符串左對齊,並使用空格填充至長度 width 的新字符串
string.rjust(width) 返回一個原字符串右對齊,並使用空格填充至長度 width 的新字符串
string.center(width) 返回一個原字符串居中,並使用空格填充至長度 width 的新字符串
5) 去除空白字符 - 3
方法 說明
string.lstrip() 截掉 string 左邊(開始)的空白字符
string.rstrip() 截掉 string 右邊(末尾)的空白字符
string.strip() 截掉 string 左右兩邊的空白字符
6) 拆分和鏈接 - 5
方法 說明
string.partition(str) 把字符串 string 分紅一個 3 元素的元組 (str前面, str, str後面)
string.rpartition(str) 相似於 partition() 方法,不過是從右邊開始查找
string.split(str="", num) 以 str 爲分隔符拆分 string,若是 num 有指定值,則僅分隔 num + 1 個子字符串,str 默認包含 '\r', '\t', '\n' 和空格
string.splitlines() 按照行('\r', '\n', '\r\n')分隔,返回一個包含各行做爲元素的列表
string.join(seq) 以 string 做爲分隔符,將 seq 中全部的元素(的字符串表示)合併爲一個新的字符串
<補>字符串的切片
  • 切片 方法適用於 字符串列表元組
    • 切片 使用 索引值 來限定範圍,從一個大的 字符串切出 小的 字符串
    • 列表元組 都是 有序 的集合,都可以 經過索引值 獲取到對應的數據
    • 字典 是一個 無序 的集合,是使用 鍵值對 保存數據

字符串索引示意圖

字符串[開始索引:結束索引:步長]
複製代碼

注意

  1. 指定的區間屬於 左閉右開[開始索引, 結束索引) => 開始索引 <= 範圍 < 結束索引
    • 起始 位開始,到 結束位的前一位 結束(不包含結束位自己)
  2. 從頭開始,開始索引 數字能夠省略,冒號不能省略
  3. 到末尾結束,結束索引 數字能夠省略,冒號不能省略
  4. 步長默認爲 1,若是連續切片,數字和冒號均可以省略
<補>索引的順序和倒序
  • 在 Python 中不只支持 順序索引,同時還支持 倒序索引
  • 所謂倒序索引就是 從右向左 計算索引:最右邊的索引值是 -1,依次遞減。
num_str = "0123456789"

# 1. 截取從 2 ~ 5 位置 的字符串
print(num_str[2:6])

# 2. 截取從 2 ~ `末尾` 的字符串
print(num_str[2:])

# 3. 截取從 `開始` ~ 5 位置 的字符串
print(num_str[:6])

# 4. 截取完整的字符串
print(num_str[:])

# 5. 從開始位置,每隔一個字符截取字符串
print(num_str[::2])

# 6. 從索引 1 開始,每隔一個取一個
print(num_str[1::2])

# 倒序切片
# -1 表示倒數第一個字符
print(num_str[-1])

# 7. 截取從 2 ~ `末尾 - 1` 的字符串
print(num_str[2:-1])

# 8. 截取字符串末尾兩個字符
print(num_str[-2:])

# 9. 字符串的逆序(面試題)
print(num_str[::-1])
複製代碼

4.4.5 公共方法和變量的高級應用

4.4.5.1 內置函數

Python 包含了如下內置函數:

函數 描述 備註
len(item) 計算容器中元素個數
del(item) 刪除變量 del 有兩種方式
max(item) 返回容器中元素最大值 若是是字典,只針對 key 比較
min(item) 返回容器中元素最小值 若是是字典,只針對 key 比較
cmp(item1, item2) 比較兩個值,-1 小於 / 0 相等 / 1 大於 Python 3.x 取消了 cmp 函數

注意字符串 比較符合如下規則: "0" < "A" < "a"。

4.4.5.2 切片
描述 Python 表達式 結果 支持的數據類型
切片 "0123456789"[::-2] "97531" 字符串、列表、元組
  • 切片 使用 索引值 來限定範圍,從一個大的 字符串切出 小的 字符串
  • 列表元組 都是 有序 的集合,都可以 經過索引值 獲取到對應的數據
  • 字典 是一個 無序 的集合,是使用 鍵值對 保存數據
4.4.5.3 運算符
運算符 Python 表達式 結果 描述 支持的數據類型
+ [1, 2] + [3, 4] [1, 2, 3, 4] 合併 字符串、列表、元組
* ["Hi!"] * 4 ['Hi!', 'Hi!', 'Hi!', 'Hi!'] 重複 字符串、列表、元組
in 3 in (1, 2, 3) True 元素是否存在 字符串、列表、元組、字典
not in 4 not in (1, 2, 3) True 元素是否不存在 字符串、列表、元組、字典
> >= == < <= (1, 2, 3) < (2, 2, 3) True 元素比較 字符串、列表、元組

注意

  • in 在對 字典 操做時,判斷的是 字典的鍵
  • innot in 被稱爲 成員運算符
4.4.5.4 內置函數完整的 for 循環語法
  • 在 Python 中完整的 for 循環 的語法以下:
for 變量 in 集合:
    
    循環體代碼
else:
    沒有經過 break 退出循環,循環結束後,會執行的代碼
複製代碼

應用場景:

  • 迭代遍歷 嵌套的數據類型時,例如 一個列表包含了多個字典
  • 需求:要判斷 某一個字典中 是否存在 指定的 值
    • 若是 存在,提示而且退出循環
    • 若是 不存在,在 循環總體結束 後,但願 獲得一個統一的提示
4.4.5.5 變量的引用
  • 變量 和 數據 都是保存在 內存 中的
  • 在 Python 中 函數 的 參數傳遞 以及 返回值 都是靠 引用 傳遞的

在 Python 中:變量數據 是分開存儲的,數據 保存在內存中的一個位置,變量 中保存着數據在內存中的地址,就叫作 引用,使用 id() 函數能夠查看變量中保存數據所在的 內存地址

注意:若是變量已經被定義,當給一個變量賦值的時候,本質上是 修改了數據的引用

  • 變量 再也不 對以前的數據引用
  • 變量 改成 對新賦值的數據引用

在 Python 中,變量的名字相似於 便籤紙 貼在 數據 上:

  • 定義一個整數變量 a,而且賦值爲 1
代碼 圖示
a = 1
  • 將變量 a 賦值爲 2
代碼 圖示
a = 2
  • 定義一個整數變量 b,而且將變量 a 的值賦值給 b
代碼 圖示
b = a

Python 中,函數的 實參/返回值 都是是靠 引用 來傳遞來的

def test(num):

    print("-" * 50)
    print("%d 在函數內的內存地址是 %x" % (num, id(num)))

    result = 100

    print("返回值 %d 在內存中的地址是 %x" % (result, id(result)))
    print("-" * 50)

    return  result

a = 10
print("調用函數前 內存地址是 %x" % id(a))

r = test(a)

print("調用函數後 實參內存地址是 %x" % id(a))
print("調用函數後 返回值內存地址是 %x" % id(r))

複製代碼

結果

4.4.5.6 可變和不可變類型
  • 不可變類型,內存中的數據不容許被修改:

    • 數字類型 int, bool, float, complex, long(2.x)
    • 字符串 str
    • 元組 tuple
  • 可變類型,內存中的數據能夠被修改:

    • 列表 list
    • 字典 dict

注意:字典的 key 只能使用不可變類型的數據

注意

  1. 可變類型的數據變化,是經過 方法 來實現的
  2. 若是給一個可變類型的變量,賦值了一個新的數據,引用會修改
    • 變量 再也不 對以前的數據引用
    • 變量 改成 對新賦值的數據引用
<補>哈希 (hash)
  • Python 中內置有一個名字叫作 hash(o) 的函數:接收一個 不可變類型 的數據做爲 參數返回 結果是一個 整數
  • 哈希 是一種 算法,其做用就是提取數據的 特徵碼(指紋)相同的內容 獲得 相同的結果不一樣的內容 獲得 不一樣的結果
  • 在 Python 中,設置字典的 鍵值對 時,會首先對 key 進行 hash 已決定如何在內存中保存字典的數據,以方便 後續 對字典的操做:增、刪、改、查:鍵值對的 key 必須是不可變類型數據,鍵值對的 value 能夠是任意類型的數據。
4.4.5.7 局部變量和全局變量
  • 局部變量 是在 函數內部 定義的變量,只能在函數內部使用;函數執行結束後,函數內部的局部變量,會被系統回收;不一樣的函數,能夠定義相同的名字的局部變量,可是 彼此之間 不會產生影響;局部變量通常臨時 保存 函數內部須要使用的數據
  • 全局變量 是在 函數外部定義 的變量(沒有定義在某一個函數內),全部函數 內部 均可以使用這個變量

提示:在其餘的開發語言中,大多 不推薦使用全局變量 —— 可變範圍太大,致使程序很差維護!

注意:函數執行時,須要處理變量時 會:

  1. 首先 查找 函數內部 是否存在 指定名稱 的局部變量,若是有,直接使用
  2. 若是沒有,查找 函數外部 是否存在 指定名稱 的全局變量,若是有,直接使用
  3. 若是尚未,程序報錯!

注意:函數不能直接修改全局變量的引用,若是要修改,必需要用global聲明該變量是全局變量。

num = 10


def demo1():

    print("demo1" + "-" * 50)

    # global 關鍵字,告訴 Python 解釋器 num 是一個全局變量
    global num
    # 只是定義了一個局部變量,不會修改到全局變量,只是變量名相同而已
    num = 100
    print(num)


def demo2():

    print("demo2" + "-" * 50)
    print(num)

demo1()
demo2()

print("over")
複製代碼

注意:爲了不局部變量和全局變量出現混淆,在定義全局變量時,有些公司會有一些開發要求,例如:全局變量名前應該增長 g_ 或者 gl_ 的前綴。

5. 判斷(分支)

5.1 if 語句語法

一、if 判斷語句基本語法:

if 要判斷的條件:
    條件成立時,要作的事情
    ……
複製代碼

注意:代碼的縮進爲一個 tab 鍵,或者 4 個空格 —— 建議使用空格

  • 在 Python 開發中,Tab 和空格不要混用!

二、若是須要在 不知足條件的時候,作某些事情,該如何作呢?

if 要判斷的條件:
    條件成立時,要作的事情
    ……
else:
    條件不成立時,要作的事情
    ……
複製代碼
  • 在程序開發中,一般 在判斷條件時,會須要同時判斷多個條件,只有多個條件都知足,纔可以執行後續代碼,這個時候須要使用到 邏輯運算符。
  • Python 中的 邏輯運算符 包括:與 and或 or非 not 三種
條件1 and 條件2 :兩個條件同時知足,返回 True
條件1 or 條件2:兩個條件只要有一個知足,返回 True
not 條件:非,不是
複製代碼

三、若是但願 再增長一些條件條件不一樣,須要執行的代碼也不一樣 時,就可使用 elif

if 條件1:
    條件1知足執行的代碼
    ……
elif 條件2:
    條件2知足時,執行的代碼
    ……
elif 條件3:
    條件3知足時,執行的代碼
    ……
else:
    以上條件都不知足時,執行的代碼
    ……
複製代碼

注意

  • elifelse 都必須和 if 聯合使用,而不能單獨使用
  • 能夠將 ifelifelse 以及各自縮進的代碼,當作一個 完整的代碼塊

四、在開發中,使用 if 進行條件判斷,若是但願 在條件成立的執行語句中增長條件判斷,就可使用 if 的嵌套

if 條件 1:
    條件 1 知足執行的代碼
    ……
    
    if 條件 1 基礎上的條件 2:
        條件 2 知足時,執行的代碼
        ……    
        
    # 條件 2 不知足的處理
    else:
        條件 2 不知足時,執行的代碼
        
# 條件 1 不知足的處理
else:
    條件1 不知足時,執行的代碼
    ……
複製代碼

5.2 if語句的應用

5.2.1 隨機數的處理
  • Python 中,要使用隨機數,首先須要導入 隨機數模塊 —— 「工具包」
import random
複製代碼
  • 導入模塊後,能夠直接在 模塊名稱 後面敲一個 . 而後按 Tab 鍵,會提示該模塊中包含的全部函數

  • random.randint(a, b) ,返回 [a, b] 之間的整數,包含 ab

  • 例如:

random.randint(12, 20)  # 生成的隨機數n: 12 <= n <= 20 
random.randint(20, 20)  # 結果永遠是 20 
random.randint(20, 10)  # 該語句是錯誤的,下限必須小於上限
複製代碼
5.2.2 石頭剪刀布
# 導入隨機工具包
# 注意:在導入工具包的時候,應該將導入的語句,放在文件的頂部
# 由於,這樣能夠方便下方的代碼,在任何須要的時候,使用工具包中的工具
import random

# 從控制檯輸入要出的拳 —— 石頭(1)/剪刀(2)/布(3)
player = int(input("請輸入您要出的拳 石頭(1)/剪刀(2)/布(3):"))

# 電腦 隨機 出拳 —— 先假定電腦只會出石頭,完成總體代碼功能
computer = random.randint(1, 3)

print("玩家選擇的拳頭是 %d - 電腦出的拳是 %d" % (player, computer))

# 比較勝負
# 1 石頭 勝 剪刀
# 2 剪刀 勝 布
# 3 布 勝 石頭
# if (()
# or ()
# or ()):
if ((player == 1 and computer == 2)
        or (player == 2 and computer == 3)
        or (player == 3 and computer == 1)):

    print("歐耶,電腦弱爆了!")
# 平局
elif player == computer:
    print("真是心有靈犀啊,再來一盤")
# 其餘的狀況就是電腦獲勝
else:
    print("不服氣,咱們決戰到天明!")
複製代碼

6. 循環

6.1 程序執行的三大流程

在程序開發中,一共有三種流程方式:

  • 順序 —— 從上向下,順序執行代碼
  • 分支 —— 根據條件判斷,決定執行代碼的 分支
  • 循環 —— 讓 特定代碼 重複 執行

程序執行的三大流程

6.2 while 循環基本使用

while 語句基本語法:

初始條件設置 —— 一般是重複執行的 計數器

while 條件(判斷 計數器 是否達到 目標次數):
    條件知足時,作的事情1
    條件知足時,作的事情2
    條件知足時,作的事情3
    ...(省略)...
    
    處理條件(計數器 + 1)
複製代碼

因爲程序員的緣由,忘記 在循環內部 修改循環的判斷條件,致使循環持續執行,程序將陷入死循環而沒法終止!

計數器 +1 :能夠經過賦值運算符簡化代碼的編寫。 常見的計數方法有兩種,能夠分別稱爲:

  • 天然計數法(從 1 開始)—— 更符合人類的習慣
  • 程序計數法(從 0 開始)—— 幾乎全部的程序語言都選擇從 0 開始計數

所以,你們在編寫程序時,應該儘可能養成習慣:除非需求的特殊要求,不然 循環 的計數都從 0 開始

6.3 break 和 continue

breakcontinue專門在循環中使用的關鍵字

  • break :某一條件知足時,退出循環,再也不執行後續的代碼
  • continue :某一條件知足時,不執行後續的代碼直接進入下一次循環
  • breakcontinue 只針對 當前所在循環 有效

6.4 while 循環嵌套

  • while 嵌套就是:while 裏面還有 while,每一次循環中還要作完一個循環。
while 條件 1:
    條件知足時,作的事情1
    條件知足時,作的事情2
    條件知足時,作的事情3
    ...(省略)...
    
    while 條件 2:
        條件知足時,作的事情1
        條件知足時,作的事情2
        條件知足時,作的事情3
        ...(省略)...
    
        處理條件 2
    
    處理條件 1
複製代碼

示例:

""" 打印 9 行小星星: * ** *** **** ***** ****** ******* ******** ********* """

# 定義起始行
row = 1

# 最大打印 9 行
while row <= 9:
    # 定義起始列
    col = 1

    # 最大打印 row 列
    while col <= row:

        # end = "",表示輸出結束後,不換行
        # "\t" 能夠在控制檯輸出一個製表符,協助在輸出文本時對齊
        print("%d * %d = %d" % (col, row, row * col), end="\t")

        # 列數 + 1
        col += 1

    # 一行打印完成的換行
    print("")

    # 行數 + 1
    row += 1

複製代碼
<補>字符串中的轉義字符
  • \t 在控制檯輸出一個 製表符,協助在輸出文本時 垂直方向 保持對齊
  • \n 在控制檯輸出一個 換行符

製表符 的功能是在不使用表格的狀況下在 垂直方向 按列對齊文本

轉義字符 描述
\\ 反斜槓符號
\' 單引號
\" 雙引號
\n 換行
\t 橫向製表符
\r 回車

7. 函數

7.1 函數的基本使用

所謂函數,就是把 具備獨立功能的代碼塊 組織爲一個小模塊,在須要的時候 調用。在開發程序時,使用函數能夠提升編寫的效率以及代碼的 重用,函數的使用包含兩個步驟: 1. 定義函數 —— 封裝 獨立的功能 2. 調用函數 —— 享受 封裝 的成果

  • 定義函數:
def 函數名():

    函數封裝的代碼
    ……
複製代碼
  • 函數調用:經過 函數名() 便可完成對函數的調用。

PyCharm 的調試工具:

  • F8 Step Over 能夠單步執行代碼,會把函數調用看做是一行代碼直接執行
  • F7 Step Into 能夠單步執行代碼,若是是函數,會進入函數內部
  • 函數的文檔註釋 在開發中,若是但願給函數添加註釋,應該在 定義函數 的下方,使用 連續的三對引號,在 連續的三對引號 之間編寫對函數的說明文字,在 函數調用 位置,使用快捷鍵 CTRL + Q 能夠查看函數的說明信息。

注意:由於 函數體相對比較獨立函數定義的上方,應該和其餘代碼(包括註釋)保留 兩個空行

7.2 函數的參數

7.2.1 形參和實參

  • 在函數名的後面的小括號內部填寫 參數,多個參數之間使用逗號 , 分隔。
  • 函數的參數,增長函數的 通用性,針對 相同的數據處理邏輯,可以 適應更多的數據**:
    • 形參定義 函數時,小括號中的參數,是用來接收參數用的,在函數內部 做爲變量使用
    • 實參調用 函數時,小括號中的參數,是用來把數據傳遞到 函數內部 用的。
def sum_2_num(num1, num2):

    result = num1 + num2
    
    print("%d + %d = %d" % (num1, num2, result))

sum_2_num(50, 20)

複製代碼

7.2.2 可變和不可變參數

問題 1:在函數內部,針對參數使用 賦值語句,會不會影響調用函數時傳遞的 實參變量? —— 不會!

  • 不管傳遞的參數是 可變 仍是 不可變
    • 只要 針對參數 使用 賦值語句,會在 函數內部 修改 局部變量的引用不會影響到 外部變量的引用

問題 2:若是傳遞的參數是 可變類型,在函數內部,使用 方法 修改了數據的內容,一樣會影響到外部的數據,例如列表變量調用 += 本質上是在執行列表變量的 extend 方法。

7.2.2 缺省參數

定義函數時,能夠給 某個參數 指定一個默認值,具備默認值的參數就叫作 缺省參數,* 調用函數時,若是沒有傳入 缺省參數 的值,則在函數內部使用定義函數時指定的 參數默認值將常見的值設置爲參數的缺省值,從而 簡化函數的調用。例如:對列表排序的方法:

gl_num_list = [6, 3, 9]

# 默認就是升序排序,由於這種應用需求更多
gl_num_list.sort()
print(gl_num_list)

# 只有當須要降序排序時,才須要傳遞 `reverse` 參數
gl_num_list.sort(reverse=True)
print(gl_num_list)
複製代碼
  • 在參數後使用賦值語句,能夠指定參數的缺省值
def print_info(name, gender=True):

    gender_text = "男生"
    if not gender:
        gender_text = "女生"

    print("%s 是 %s" % (name, gender_text))
複製代碼

提示

  1. 缺省參數,須要使用 最多見的值 做爲默認值!
  2. 若是一個參數的值 不能肯定,則不該該設置默認值,具體的數值在調用函數時,由外界傳遞!

注意

  1. 必須保證 帶有默認值的缺省參數 在參數列表末尾
  2. 調用函數時,若是有 多個參數須要指定參數名,這樣解釋器纔可以知道參數的對應關係!

7.2.3 多值參數

有時可能須要 一個函數 可以處理的參數 個數 是不肯定的,這個時候,就可使用 多值參數

  • python 中有 兩種 多值參數:
    • 參數名前增長 一個 * 能夠接收 元組
    • 參數名前增長 兩個 ** 能夠接收 字典
  • 通常在給多值參數命名時,習慣使用如下兩個名字
    • *args —— 存放 元組 參數,前面有一個 *
    • **kwargs —— 存放 字典 參數,前面有兩個 **
def demo(num, *args, **kwargs):

    print(num)
    print(args)
    print(kwargs)


demo(1, 2, 3, 4, 5, name="小明", age=18, gender=True)
print("-"*20)
demo(1,(2,3,4,5),{"name":"小明", "age":18, "gender":True})
print("-"*20)
demo(1,(2,3,4,5), name="小明", age=18, gender=True)

複製代碼

結果

<補> 元組和字典的拆包
  • 在調用帶有多值參數的函數時,若是但願:
    • 將一個 元組變量,直接傳遞給 args
    • 將一個 字典變量,直接傳遞給 kwargs
  • 就可使用 拆包,簡化參數的傳遞,拆包 的方式是:
    • 元組變量前,增長 一個 *
    • 字典變量前,增長 兩個 *
def demo(*args, **kwargs):

    print(args)
    print(kwargs)

# 須要將一個元組變量/字典變量傳遞給函數對應的參數
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}

# 會把 num_tuple 和 xiaoming 做爲元組傳遞個 args
# demo(gl_nums, gl_xiaoming)
demo(*gl_nums, **gl_xiaoming)

複製代碼

7.3 函數的返回值

  • 在函數中使用 return 關鍵字能夠返回結果,調用函數一方,能夠 使用變量接收 函數的返回結果。

注意:return 表示返回,後續的代碼都不會被執行

def sum_2_num(num1, num2):
    """對兩個數字的求和"""

    return num1 + num2

# 調用函數,並使用 result 變量接收計算結果
result = sum_2_num(10, 20)

print("計算結果是 %d" % result)

複製代碼

函數參數和返回值

技巧

  • Python 中,能夠 將一個元組 使用 賦值語句 同時賦值給 多個變量
  • 注意:變量的數量須要和元組中的元素數量保持一致
# Python 專有,利用元組交換兩個變量的值
a, b = b, a
複製代碼

7.4 函數的嵌套調用

  • 一個函數裏面 又調用另一個函數,這就是 函數嵌套調用
def test1():

    print("*" * 50)
    print("test 1")
    print("*" * 50)


def test2():

    print("-" * 50)
    print("test 2")
    
    test1()
    
    print("-" * 50)

test2()

複製代碼

提示:工做中針對需求的變化,應該冷靜思考,不要輕易修改以前已經完成的,可以正常執行的函數

7.5 函數的遞歸

函數調用自身的 編程技巧 稱爲遞歸

特色一個函數 內部 調用本身

代碼特色

  1. 函數內部的 代碼 是相同的,只是針對 參數 不一樣,處理的結果不一樣
  2. 參數知足一個條件 時,函數再也不執行,一般被稱爲遞歸的出口,不然 會出現死循環

案例 —— 計算數字累加

需求:

  1. 定義一個函數 sum_numbers
  2. 可以接收一個 num 的整數參數
  3. 計算 1 + 2 + ... num 的結果
def sum_numbers(num):

    if num == 1:
        return 1
    
    # 假設 sum_numbers 可以完成 num - 1 的累加
    temp = sum_numbers(num - 1)

    # 函數內部的核心算法就是 兩個數字的相加
    return num + temp

print(sum_numbers(2))

複製代碼

7.6 在模塊中定義函數

模塊是 Python 程序架構的一個核心概念

模塊 就比如是 工具包,要想使用這個工具包中的工具,就須要 導入 import 這個模塊, 每個以擴展名 py 結尾的 Python 源代碼文件都是一個 模塊,在模塊中定義的 全局變量函數 都是模塊可以提供給外界直接使用的工具。

  • 能夠 在一個 Python 文件定義 變量 或者 函數
  • 而後在 另一個文件中 使用 import 導入這個模塊
  • 導入以後,就可使用 模塊名.變量 / 模塊名.函數 的方式,使用這個模塊中定義的變量或者函數

模塊可讓 曾經編寫過的代碼 方便的被 複用! 模塊名也是一個標識符,若是在給 Python 文件起名時,以數字開頭 是沒法在 PyCharm 中經過導入這個模塊的。

<補> Pyc 文件

Ccompiled 編譯過 的意思。

  • 瀏覽程序目錄會發現一個 __pycache__ 的目錄。
  • 目錄下會有一個 hm_10_分隔線模塊.cpython-35.pyc 文件,cpython-35 表示 Python 解釋器的版本。
  • 這個 pyc 文件是由 Python 解釋器將 模塊的源碼 轉換爲 字節碼

Python 這樣保存 字節碼 是做爲一種啓動 速度的優化

  • 什麼是字節碼
  • Python 在解釋源程序時是分紅兩個步驟的:
    1. 首先處理源代碼,編譯 生成一個二進制 字節碼
    2. 再對 字節碼 進行處理,纔會生成 CPU 可以識別的 機器碼
  • 有了模塊的字節碼文件以後,下一次運行程序時,若是在 上次保存字節碼以後 沒有修改過源代碼,Python 將會加載 .pyc 文件並跳過編譯這個步驟。
  • Python 重編譯時,它會自動檢查源文件和字節碼文件的時間戳。
  • 若是你又修改了源代碼,下次程序運行時,字節碼將自動從新建立。

8. 文件、異常、模塊和包

8.1 文件

8.1.1 文件的概念

  • 計算機的 文件,就是存儲在某種 長期儲存設備 上的一段 數據
  • 長期存儲設備包括:硬盤、U 盤、移動硬盤、光盤...

文件的做用:將數據長期保存下來,在須要的時候使用 文件的存儲方式:在計算機中,文件是以 二進制 的方式保存在磁盤上的 文本文件:可使用 文本編輯軟件 查看,本質上仍是二進制文件 二進制文件:保存的內容 不是給人直接閱讀的,而是 提供給其餘軟件使用的,例如:圖片文件、音頻文件、視頻文件等等,二進制文件不能使用 文本編輯軟件 查看

8.1.2 文件的基本操做

操做文件的套路: 在 計算機 中要操做文件的套路很是固定,一共包含三個步驟

  1. 打開文件
  2. 讀、寫文件
    • 將文件內容讀入內存
    • 將內存內容寫入文件
  3. 關閉文件

操做文件的函數/方法Python 中要操做文件須要記住 1 個函數和 3 個方法

序號 函數/方法 說明
01 open 打開文件,而且返回文件操做對象
02 read 將文件內容讀取到內存
03 write 將指定內容寫入文件
04 close 關閉文件

open 函數負責打開文件,而且返回文件對象 read/write/close 三個方法都須要經過 文件對象 來調用

讀取文件示例

open 函數的第一個參數是要打開的文件名(文件名區分大小寫) 若是文件 存在,返回 文件操做對象 若是文件 不存在,會 拋出異常

read 方法能夠一次性 讀入返回 文件的 全部內容

close 方法負責 關閉文件 若是 忘記關閉文件會形成系統資源消耗,並且會影響到後續對文件的訪問

注意read 方法執行後,會把 文件指針 移動到 文件的末尾

# 1\. 打開 - 文件名須要注意大小寫
file = open("README")

# 2\. 讀取
text = file.read()
print(text)

# 3\. 關閉
file.close()

複製代碼

提示

  • 在開發中,一般會先編寫 打開關閉 的代碼,再編寫中間針對文件的 讀/寫 操做!
<補> 文件指針
  • 文件指針 標記 從哪一個位置開始讀取數據
  • 第一次打開 文件時,一般 文件指針會指向文件的開始位置
  • 當執行了 read 方法後,文件指針 會移動到 讀取內容的末尾
    • 默認狀況下會移動到 文件末尾

思考:若是執行了一次 read 方法,讀取了全部內容,那麼再次調用 read 方法,還可以得到到內容嗎? 答案:不能!第一次讀取以後,文件指針移動到了文件末尾,再次調用不會讀取到任何的內容。

<補> 打開文件的方式
  • open 函數默認以 只讀方式 打開文件,而且返回文件對象
f = open("文件名", "訪問方式")
複製代碼
訪問方式 說明
r 只讀方式打開文件。文件的指針將會放在文件的開頭,這是默認模式。若是文件不存在,拋出異常
w 只寫方式打開文件。若是文件存在會被覆蓋。若是文件不存在,建立新文件
a 追加方式打開文件。若是該文件已存在,文件指針將會放在文件的結尾。若是文件不存在,建立新文件進行寫入
r+ 讀寫方式打開文件。文件的指針將會放在文件的開頭。若是文件不存在,拋出異常
w+ 讀寫方式打開文件。若是文件存在會被覆蓋。若是文件不存在,建立新文件
a+ 讀寫方式打開文件。若是該文件已存在,文件指針將會放在文件的結尾。若是文件不存在,建立新文件進行寫入

提示:頻繁的移動文件指針,會影響文件的讀寫效率,開發中更多的時候會以 只讀只寫 的方式來操做文件

寫入文件示例

# 打開文件
f = open("README", "w")

f.write("hello python!\n")
f.write("今每天氣真好")

# 關閉文件
f.close()

複製代碼
<補> 按行讀取文件內容:readline()
  • read 方法默認會把文件的 全部內容 一次性讀取到內存
  • 若是文件太大,對內存的佔用會很是嚴重
  • readline 方法能夠一次讀取一行內容
  • 方法執行後,會把 文件指針 移動到下一行,準備再次讀取

讀取大文件的正確姿式

# 打開文件
file = open("README")

while True:
    # 讀取一行內容
    text = file.readline()

    # 判斷是否讀到內容
    if not text:
        break

    # 每讀取一行的末尾已經有了一個 `\n`
    print(text, end="")

# 關閉文件
file.close()

複製代碼

複製大文件

  • 打開一個已有文件,逐行讀取內容,並順序寫入到另一個文件
# 1\. 打開文件
file_read = open("README")
file_write = open("README[復件]", "w")

# 2\. 讀取並寫入文件
while True:
    # 每次讀取一行
    text = file_read.readline()

    # 判斷是否讀取到內容
    if not text:
        break

    file_write.write(text)

# 3\. 關閉文件
file_read.close()
file_write.close()

複製代碼

8.1.3 文件/目錄的經常使用管理操做

  • 終端 / 文件瀏覽器、 中能夠執行常規的 文件 / 目錄 管理操做,例如:
    • 建立、重命名、刪除、改變路徑、查看目錄內容、……
  • 在 Python 中,若是但願經過程序實現上述功能,須要導入 os 模塊

文件管理操做

序號 方法名 說明 示例
01 rename 重命名文件 os.rename(源文件名, 目標文件名)
02 remove 刪除文件 os.remove(文件名)

目錄管理操做

序號 方法名 說明 示例
01 listdir 目錄列表 os.listdir(目錄名)
02 mkdir 建立目錄 os.mkdir(目錄名)
03 rmdir 刪除目錄 os.rmdir(目錄名)
04 getcwd 獲取當前目錄 os.getcwd()
05 chdir 修改工做目錄 os.chdir(目標目錄)
06 path.isdir 判斷是不是文件 os.path.isdir(文件路徑)

提示:文件或者目錄操做都支持 相對路徑絕對路徑

8.1.4 文本文件的編碼格式

  • 文本文件存儲的內容是基於 字符編碼 的文件,常見的編碼有 ASCII 編碼,UNICODE 編碼等

Python 2.x 默認使用 ASCII 編碼格式 Python 3.x 默認使用 UTF-8 編碼格式

ASCII 編碼

  • 計算機中只有 256ASCII 字符
  • 一個 ASCII 在內存中佔用 1 個字節 的空間
    • 80/1 的排列組合方式一共有 256 種,也就是 2 ** 8

UTF-8 編碼格式

  • 計算機中使用 1~6 個字節 來表示一個 UTF-8 字符,涵蓋了 地球上幾乎全部地區的文字
  • 大多數漢字會使用 3 個字節 表示
  • UTF-8UNICODE 編碼的一種編碼格式

Ptyhon 2.x 中如何使用中文?

  • 在 Python 2.x 文件的 第一行 增長代碼# *-* coding:utf8 *-*,解釋器會以 utf-8 編碼來處理 python 文件,這方式是官方推薦使用的!
  • 也可使用# coding=utf8

Python 2.x 中如何正確遍歷 unicode 字符串?

  • 在 Python 2.x 中,即便指定了文件使用 UTF-8 的編碼格式,可是在遍歷字符串時,仍然會 以字節爲單位遍歷 字符串
  • 要可以 正確的遍歷字符串,在定義字符串時在字符串的引號前,增長一個小寫字母 u,告訴解釋器這是一個 unicode 字符串(使用 UTF-8 編碼格式的字符串)
# *-* coding:utf8 *-*

# 在字符串前,增長一個 `u` 表示這個字符串是一個 utf8 字符串
hello_str = u"你好世界"

print(hello_str)

for c in hello_str:
    print(c)

複製代碼

8.2 異常

8.2.1 異常的概念

  • 程序在運行時,若是 Python 解釋器 遇到 到一個錯誤,會中止程序的執行,而且提示一些錯誤信息,這就是 異常
  • 程序中止執行而且提示錯誤信息 這個動做,咱們一般稱之爲:拋出(raise)異常

程序開發時,很難將 全部的特殊狀況 都處理的面面俱到,經過 異常捕獲 能夠針對突發事件作集中的處理,從而保證程序的 穩定性和健壯性

8.2.2 捕獲異常:try except else finally

簡單的捕獲異常語法

  • 在程序開發中,若是 對某些代碼的執行不能肯定是否正確,能夠增長 try(嘗試)捕獲異常
try:
    嘗試執行的代碼
except:
    出現錯誤的處理
複製代碼
  • try 嘗試,下方編寫要嘗試代碼,不肯定是否可以正常執行的代碼
  • except 若是不是,下方編寫嘗試失敗的代碼

簡單異常捕獲1 —— 要求用戶輸入整數

try:
    # 提示用戶輸入一個數字
    num = int(input("請輸入數字:"))
except:
    print("請輸入正確的數字")

複製代碼

錯誤類型捕獲

  • 在程序執行時,可能會遇到 不一樣類型的異常,而且須要 針對不一樣類型的異常,作出不一樣的響應,這個時候,就須要捕獲錯誤類型了
try:
    # 嘗試執行的代碼
    pass
except 錯誤類型1:
    # 針對錯誤類型1,對應的代碼處理
    pass
except (錯誤類型2, 錯誤類型3):
    # 針對錯誤類型2 和 3,對應的代碼處理
    pass
except Exception as result:
    print("未知錯誤 %s" % result)
複製代碼
  • Python 解釋器 拋出異常 時,最後一行錯誤信息的第一個單詞,就是錯誤類型

異常類型捕獲2 —— 要求用戶輸入整數

try:
    num = int(input("請輸入整數:"))
    result = 8 / num
    print(result)
except ValueError:
    print("請輸入正確的整數")
except ZeroDivisionError:
    print("除 0 錯誤")
複製代碼

捕獲未知錯誤

  • 在開發時,要預判到全部可能出現的錯誤,仍是有必定難度的
  • 若是但願程序 不管出現任何錯誤,都不會由於 Python 解釋器 拋出異常而被終止,能夠再增長一個 except

語法以下:

except Exception as result:
    print("未知錯誤 %s" % result)
複製代碼

異常捕獲完整語法

  • 在實際開發中,爲了可以處理複雜的異常狀況,完整的異常語法以下:
try:
    # 嘗試執行的代碼
    pass
except 錯誤類型1:
    # 針對錯誤類型1,對應的代碼處理
    pass
except 錯誤類型2:
    # 針對錯誤類型2,對應的代碼處理
    pass
except (錯誤類型3, 錯誤類型4):
    # 針對錯誤類型3 和 4,對應的代碼處理
    pass
except Exception as result:
    # 打印錯誤信息
    print(result)
else:
    # 沒有異常纔會執行的代碼
    pass
finally:
    # 不管是否有異常,都會執行的代碼
    print("不管是否有異常,都會執行的代碼")

複製代碼
  • else 只有在沒有異常時纔會執行的代碼

  • finally 不管是否有異常,都會執行的代碼

  • 以前一個演練的 完整捕獲異常 的代碼以下:

try:
    num = int(input("請輸入整數:"))
    result = 8 / num
    print(result)
except ValueError:
    print("請輸入正確的整數")
except ZeroDivisionError:
    print("除 0 錯誤")
except Exception as result:
    print("未知錯誤 %s" % result)
else:
    print("正常執行")
finally:
    print("執行完成,可是不保證正確")

複製代碼

8.2.3 異常的傳遞

  • 異常的傳遞 —— 當 函數/方法 執行 出現異常,會 將異常傳遞 給 函數/方法 的 調用一方
  • 若是 傳遞到主程序,仍然 沒有異常處理,程序纔會被終止

提示:

  • 在開發中,能夠在主函數中增長 異常捕獲,而在主函數中調用的其餘函數,只要出現異常,都會傳遞到主函數的異常捕獲中
  • 這樣就不須要在代碼中,增長大量的異常捕獲,可以保證代碼的整潔
''' 需求: 1. 定義函數 `demo1()` **提示用戶輸入一個整數而且返回** 2. 定義函數 `demo2()` 調用 `demo1()` 3. 在主程序中調用 `demo2()` '''
def demo1():
    return int(input("請輸入一個整數:"))

def demo2():
    return demo1()

try:
    print(demo2())
except ValueError:
    print("請輸入正確的整數")
except Exception as result:
    print("未知錯誤 %s" % result)

複製代碼

8.2.4 拋出異常: raise

應用場景

  • 在開發中,除了 代碼執行出錯 Python 解釋器會 拋出 異常以外
  • 還能夠根據 應用程序 特有的業務需求 主動拋出異常

示例

  • 提示用戶 輸入密碼,若是 長度少於 8,拋出 異常

拋出異常

  • Python 中提供了一個 Exception 異常類
  • 在開發時,若是知足 特定業務需求時,但願 拋出異常,由其餘須要處理的函數捕獲異常,能夠:
    1. 建立 一個 Exception對象
    2. 使用 raise 關鍵字 拋出 異常對象
''' **需求** * 定義 `input_password` 函數,提示用戶輸入密碼 * 若是用戶輸入長度 < 8,拋出異常 * 若是用戶輸入長度 >=8,返回輸入的密碼 '''
def input_password():

    # 1\. 提示用戶輸入密碼
    pwd = input("請輸入密碼:")

    # 2\. 判斷密碼長度,若是長度 >= 8,返回用戶輸入的密碼
    if len(pwd) >= 8:
        return pwd

    # 3\. 密碼長度不夠,須要拋出異常
    # 1> 建立異常對象 - 使用異常的錯誤信息字符串做爲參數
    ex = Exception("密碼長度不夠")

    # 2> 拋出異常對象
    raise ex

try:
    user_pwd = input_password()
    print(user_pwd)
except Exception as result:
    print("發現錯誤:%s" % result)

複製代碼

8.3 模塊和包

8.3.1 模塊

模塊是 Python 程序架構的一個核心概念

  • 每個以擴展名 py 結尾的 Python 源代碼文件都是一個 模塊
  • 模塊名 一樣也是一個 標識符,須要符合標識符的命名規則
  • 在模塊中定義的 全局變量函數 都是提供給外界直接使用的 工具
  • 模塊 就比如是 工具包,要想使用這個工具包中的工具,就須要先 導入 這個模塊

模塊的兩種導入方式

1)import 導入

import 模塊名1, 模塊名2 

複製代碼

提示:在導入模塊時,每一個導入應該獨佔一行

import 模塊名1
import 模塊名2 

複製代碼
  • 導入以後
    • 經過 模塊名. 使用 模塊提供的工具 —— 全局變量函數
  • 使用 as 指定模塊的別名

若是模塊的名字太長,可使用 as 指定模塊的名稱,以方便在代碼中的使用

import 模塊名1 as 模塊別名
複製代碼

注意:模塊別名 應該符合 大駝峯命名法

2)from...import 導入

  • 若是但願 從某一個模塊 中,導入 部分 工具,就可使用 from ... import 的方式
  • import 模塊名一次性 把模塊中 全部工具所有導入,而且經過 模塊名/別名 訪問
# 從 模塊 導入 某一個工具
from 模塊名1 import 工具名
複製代碼
  • 導入以後
    • 能夠直接使用 模塊提供的工具 —— 全局變量函數

注意

若是 兩個模塊,存在 同名的函數,那麼 後導入模塊的函數,會 覆蓋掉先導入的函數

  • 開發時 import 代碼應該統一寫在 代碼的頂部,更容易及時發現衝突
  • 一旦發現衝突,可使用 as 關鍵字 給其中一個工具起一個別名
  • from...import *
# 從 模塊 導入 全部工具
from 模塊名1 import *
複製代碼

注意

這種方式不推薦使用,由於函數重名並無任何的提示,出現問題很差排查

<補> 模塊的搜索順序

Python 的解釋器在 導入模塊 時,會:

  1. 搜索 當前目錄 指定模塊名的文件,若是有就直接導入
  2. 若是沒有,再搜索 系統目錄

在開發時,給文件起名,不要和 系統的模塊文件 重名

Python 中每個模塊都有一個內置屬性 __file__ 能夠 查看模塊完整路徑

原則 —— 每個文件都應該是能夠被導入的

  • 一個 獨立的 Python 文件 就是一個 模塊
  • 在導入文件時,文件中 全部沒有任何縮進的代碼 都會被執行一遍!

實際開發場景

  • 在實際開發中,每個模塊都是獨立開發的,大多都有專人負責
  • 開發人員 一般會在 模塊下方 增長一些測試代碼
    • 僅在模塊內使用,而被導入到其餘文件中不須要執行

__name__ 屬性

  • __name__ 屬性能夠作到,測試模塊的代碼 只在測試狀況下被運行,而在 被導入時不會被執行
  • __name__Python 的一個內置屬性,記錄着一個 字符串
  • 若是 是被其餘文件導入的__name__ 就是 模塊名
  • 若是 是當前執行的程序 __name____main__

在不少 Python 文件中都會看到如下格式的代碼

# 導入模塊
# 定義全局變量
# 定義類
# 定義函數

# 在代碼的最下方
def main():
    # ...
    pass

# 根據 __name__ 判斷是否執行下方代碼
if __name__ == "__main__":
    main()

複製代碼

8.3.2 包(Package)

  • 是一個 包含多個模塊特殊目錄
  • 目錄下有一個 特殊的文件 __init__.py
  • 包名的 命名方式 和變量名一致,小寫字母 + _

好處:使用 import 包名 能夠一次性導入 全部的模塊

案例

  1. 新建一個 hm_message
  2. 在目錄下,新建兩個文件 send_messagereceive_message
  3. send_message 文件中定義一個 send 函數
  4. receive_message 文件中定義一個 receive 函數
  5. 在外部直接導入 hm_message 的包

__init__.py

  • 要在外界使用 中的模塊,須要在 __init__.py 中指定 對外界提供的模塊列表
# 從 當前目錄 導入 模塊列表
from . import send_message
from . import receive_message
複製代碼

8.3.3 發佈模塊

  • 若是但願本身開發的模塊,分享 給其餘人,能夠按照如下步驟操做:
  1. 建立 setup.py
  • setup.py 的文件
from distutils.core import setup

setup(name="hm_message",  # 包名
      version="1.0",  # 版本
      description="itheima's 發送和接收消息模塊",  # 描述信息
      long_description="完整的發送和接收消息模塊",  # 完整描述信息
      author="itheima",  # 做者
      author_email="itheima@itheima.com",  # 做者郵箱
      url="www.itheima.com",  # 主頁
      py_modules=["hm_message.send_message",
                  "hm_message.receive_message"])

複製代碼

有關字典參數的詳細信息,能夠參閱官方網站:docs.python.org/2/distutils…

  1. 構建模塊
$ python3 setup.py build
複製代碼
  1. 生成發佈壓縮包
$ python3 setup.py sdist
複製代碼

注意:要製做哪一個版本的模塊,就使用哪一個版本的解釋器執行!

4)安裝模塊

$ tar -zxvf hm_message-1.0.tar.gz 
$ sudo python3 setup.py install
複製代碼

5)卸載模塊

直接從安裝目錄下,把安裝模塊的 目錄 刪除就能夠

$ cd /usr/local/lib/python3.5/dist-packages/
$ sudo rm -r hm_message*
複製代碼

pip 安裝第三方模塊

  • 第三方模塊 一般是指由 知名的第三方團隊 開發的 而且被 程序員普遍使用Python 包 / 模塊
    • 例如 pygame 就是一套很是成熟的 遊戲開發模塊
  • pip 是一個現代的,通用的 Python 包管理工具
  • 提供了對 Python 包的查找、下載、安裝、卸載等功能

安裝和卸載命令以下:

# 將模塊安裝到 Python 2.x 環境
$ sudo pip install pygame
$ sudo pip uninstall pygame

# 將模塊安裝到 Python 3.x 環境
$ sudo pip3 install pygame
$ sudo pip3 uninstall pygame
複製代碼

第二部分 面向對象

第二部分 面向對象

1. 面向對象(OOP)基本概念

面向對象編程 —— Object Oriented Programming 簡寫 OOP

    1. 面向過程 —— 怎麼作? 把完成某一個需求的 全部步驟 從頭至尾 逐步實現 根據開發需求,將某些 功能獨立 的代碼 封裝 成一個又一個 函數 最後完成的代碼,就是順序地調用 不一樣的函數 特色: 注重 步驟與過程,不注重職責分工 若是需求複雜,代碼會變得很複雜 開發複雜項目,沒有固定的套路,開發難度很大!
    1. 面向對象 —— 誰來作?

      相比較函數,面向對象 是更大的封裝,根據職責在 一個對象中封裝多個方法

      在完成某一個需求前,首先肯定 職責 —— 要作的事情(方法) 根據 職責 肯定不一樣的 對象,在 對象 內部封裝不一樣的 方法(多個) 最後完成的代碼,就是順序地讓 不一樣的對象 調用 不一樣的方法 特色: 注重 對象和職責,不一樣的對象承擔不一樣的職責 更加適合應對複雜的需求變化,是專門應對複雜項目開發,提供的固定套路 須要在面向過程基礎上,再學習一些面向對象的語法

    1. 類和對象 是對一羣具備 相同 特徵 或者 行爲 的事物的一個統稱,是抽象的,特徵 被稱爲 屬性,行爲 被稱爲 方法對象 是 由類建立出來的一個具體存在,是類的實例化。 在程序開發中,要設計一個類,一般須要知足一下三個要素:
      • 類名 這類事物的名字,知足大駝峯命名法
      • 屬性 這類事物具備什麼樣的特徵
      • 方法 這類事物具備什麼樣的行爲

2. 面向對象基礎語法

2.1 dir 內置函數和內置方法

在 Python 中 對象幾乎是無所不在的,咱們以前學習的 變量、數據、函數 都是對象。 在 Python 中可使用如下兩個方法驗證:

  • 標識符 / 數據 後輸入一個點 .,而後按下 TAB 鍵,iPython 會提示該對象可以調用的方法列表。
  • 使用內置函數 dir 傳入 標識符 / 數據,能夠查看對象內的 全部屬性及方法。 提示__方法名__格式的方法是 Python 提供的 內置方法 / 屬性
序號 方法名 類型 做用
01 __new__ 方法 建立對象時,會被 自動 調用
02 __init__ 方法 對象被初始化時,會被 自動 調用
03 __del__ 方法 對象被從內存中銷燬前,會被 自動 調用
04 __str__ 方法 返回對象的描述信息,print 函數輸出使用

提示 利用好 dir() 函數,在學習時不少內容就不須要死記硬背了。

2.2 定義簡單的類(只包含方法)

面向對象是更大的封裝,在 一個類中封裝多個方法,這樣經過這個類建立出來的對象,就能夠直接調用這些方法了!

定義一個只包含方法的類:

class 類名:

    def 方法1(self, 參數列表):
        pass
    
    def 方法2(self, 參數列表):
        pass
複製代碼

方法 的定義格式和以前學習過的函數幾乎同樣,區別在於第一個參數必須是 self注意類名 的 命名規則 要符合 大駝峯命名法。 當一個類定義完成以後,要使用這個類來建立對象,語法格式以下:

對象變量 = 類名()
複製代碼

在面向對象開發中,引用的概念是一樣適用的!

使用 print輸出 對象變量,默認狀況下,是可以輸出這個變量 引用的對象 是 由哪個類建立的對象,以及 在內存中的地址(十六進制表示)。

提示:在計算機中,一般使用 十六進制 表示 內存地址。

若是在開發中,但願使用 print輸出 對象變量 時,可以打印 自定義的內容,就能夠利用 __str__這個內置方法了:

class Cat:

    def __init__(self, new_name):

        self.name = new_name

        print("%s 來了" % self.name)

    def __del__(self):

        print("%s 去了" % self.name)

    def __str__(self):
        return "我是小貓:%s" % self.name

tom = Cat("Tom")
print(tom)
複製代碼

注意:__str__方法必須返回一個字符串。

2.3 方法中的 self 參數

在 Python 中,要 給對象設置屬性,很是的容易,只須要在 類的外部的代碼 中直接經過 對象.設置一個屬性便可,可是不推薦使用:

class Cat:
    """這是一個貓類"""

    def eat(self):
        print("小貓愛吃魚")

    def drink(self):
        print("小貓在喝水")

tom = Cat()
# 給對象設置屬性
tom.name = "Tom"

複製代碼

由於:對象屬性的封裝應該封裝在類的內部

由哪個對象調用的方法,方法內的 self就是哪個對象的引用

  • 在類封裝的方法內部,self 就表示當前調用方法的對象本身,在方法內部: 能夠經過 self.訪問對象的屬性,也能夠經過 self.調用對象的其餘方法。
  • 調用方法時,程序員不須要傳遞 self 參數。
  • 在 類的外部,經過變量名.訪問對象的 屬性和方法 在 類封裝的方法中,經過 self.訪問對象的 屬性和方法

2.4 初始化方法:__init__

  • 當使用 類名() 建立對象時,會 自動 執行如下操做: 爲對象在內存中分配空間 —— 建立對象 爲對象的屬性設置初始值 —— 初始化方法(__init__)

__init__ 方法是 專門 用來定義一個類具備哪些屬性的方法!

  • __init__ 方法內部使用 self.屬性名 = 屬性的初始值 就能夠定義屬性,定義屬性以後,再使用 類建立的對象,都會擁有該屬性。

  • 在開發中,若是但願在 建立對象的同時,就設置對象的屬性,能夠對 __init__ 方法進行 改造

    1. 把但願設置的屬性值,定義成 __init__方法的參數
    2. 在方法內部使用 self.屬性 = 形參 接收外部傳遞的參數
    3. 在建立對象時,使用 類名(屬性1, 屬性2...) 調用
class Cat:

    def __init__(self, name):
        print("初始化方法 %s" % name)
        self.name = name
    ...
    
tom = Cat("Tom")
...

lazy_cat = Cat("大懶貓")
...
複製代碼

2.5 私有屬性和私有方法

應用場景

  • 在實際開發中,對象某些屬性或方法 可能只但願 在對象的內部被使用,而 不但願在外部被訪問到
  • 私有屬性 就是 對象 不但願公開的 屬性
  • 私有方法 就是 對象 不但願公開的 方法

定義方式

  • 定義屬性或方法時,在 屬性名或者方法名前 增長 兩個下劃線,定義的就是 私有 屬性或方法:

私有屬性和私有方法

僞私有屬性和私有方法 Python 中,並無 真正意義 的 私有 在給 屬性、方法 命名時,實際是對名稱作了一些特殊處理,使得外界沒法訪問到 處理方式:在 名稱 前面加上_類名 => _類名__名稱

# 私有屬性,外部不能直接訪問到
print(xiaofang._Women__age)

# 私有方法,外部不能直接調用
xiaofang._Women__secret()
複製代碼

提示:在平常開發中,不要使用這種方式,訪問對象的 私有屬性 或 私有方法。

3. 封裝、繼承和多態

面向對象三大特性:

  1. 封裝 根據 職責 將 屬性 和 方法 封裝 到一個抽象的 類 中
  2. 繼承 實現代碼的重用,相同的代碼不須要重複的編寫
  3. 多態 不一樣的對象調用相同的方法,產生不一樣的執行結果,增長代碼的靈活度

3.1 繼承

3.1.1 單繼承

繼承的概念:子類 擁有 父類 以及 父類的父類 中封裝的全部 屬性 和 方法。

class 類名(父類名):

    pass
複製代碼

當 父類 的方法實現不能知足子類需求時,能夠對方法進行重寫(override) 重寫 父類方法有兩種狀況:

  1. 覆蓋 父類的方法:父類的方法實現 和 子類的方法實現徹底不一樣 具體的實現方式,就至關於在 子類中 定義了一個 和父類同名的方法而且實現。
  2. 對父類方法進行 擴展:子類的方法實現 中 包含 父類的方法實現 在子類中 重寫 父類的方法;在須要的位置使用 super().父類方法 來調用父類方法的執行代碼;其餘的位置針對子類的需求,編寫 子類特有的代碼實現。
關於 super
  • 在 Python 中 super 是一個 特殊的類
  • super()就是使用 super 類建立出來的對象
  • 最常 使用的場景就是在 重寫父類方法時,調用 在父類中封裝的方法實現

調用父類方法的另一種方式:在 Python 2.x 時,若是須要調用父類的方法,還可使用如下方式:父類名.方法(self)。目前在 Python 3.x 還支持這種方式,但不推薦使用,由於一旦 父類發生變化,方法調用位置的 類名 一樣須要修改。

父類的 私有屬性 和 私有方法

子類對象 不能 在本身的方法內部,直接 訪問 父類的 私有屬性 或 私有方法 子類對象 能夠經過 父類 的 公有方法 間接 訪問到 私有屬性 或 私有方法

  • 私有屬性、方法 是對象的隱私,不對外公開,外界 以及 子類 都不能直接訪問
  • 私有屬性、方法 一般用於作一些內部的事情

3.1.2 多繼承

子類 能夠擁有 多個父類,而且具備 全部父類 的 屬性 和 方法,例如:孩子 會繼承本身 父親 和 母親 的 特性。

class 子類名(父類名1, 父類名2...):
    pass
複製代碼
Python 中的 MRO算法(Method Resolution Order)
  • 若是 不一樣的父類 中存在 同名的方法,子類對象 在調用方法時,會調用 哪個父類中的方法呢? 提示:開發時,應該儘可能避免這種容易產生混淆的狀況! —— 若是 父類之間 存在 同名的屬性或者方法,應該 儘可能避免使用多繼承
  • Python 中針對 類 提供了一個 內置屬性__mro__ 能夠查看 方法 搜索順序
    • 在搜索方法時,是按照 mro 的輸出結果 從左至右 的順序查找的
    • 若是在當前類中 找到方法,就直接執行,再也不搜索
    • 若是 沒有找到,就查找下一個類 中是否有對應的方法,若是找到,就直接執行,再也不搜索
    • 若是找到最後一個類,尚未找到方法,程序報錯

MRO 是 method resolution order —— 方法搜索順序,主要用於 在多繼承時判斷 方法、屬性 的調用 路徑

新式類與舊式(經典)類
  • 新式類:以 object 爲基類的類,推薦使用
  • 經典類:不以 object爲基類的類,不推薦使用

在 Python 3.x 中定義類時,若是沒有指定父類,會 默認使用 object做爲該類的 基類 —— Python 3.x 中定義的類都是 新式類,在 Python 2.x 中定義類時,若是沒有指定父類,則不會以 object 做爲 基類。

  • 爲了保證編寫的代碼可以同時在 Python 2.x 和 Python 3.x 運行!從此在定義類時,若是沒有父類,建議統一繼承自 object:
class 類名(object):
    pass
複製代碼

object 是 Python 爲全部對象提供的 基類,提供有一些內置的屬性和方法,可使用 dir(object) 函數查看。

3.2 多態

面向對象三大特性:

  1. 封裝 根據 職責 將 屬性 和 方法 封裝 到一個抽象的 類 中
    • 定義類的準則
  2. 繼承 實現代碼的重用,相同的代碼不須要重複的編寫
    • 設計類的技巧
    • 子類針對本身特有的需求,編寫特定的代碼
  3. 多態 不一樣的 子類對象 調用相同的 父類方法,產生不一樣的執行結果
    • 增長代碼的靈活度
    • 以 繼承 和 重寫父類方法 爲前提
    • 調用方法的技巧,不會影響到類的內部設計

多態 更容易編寫出出通用的代碼,作出通用的編程,以適應需求的不斷變化!

案例: 在 Dog 類中封裝方法 game:普通狗只是簡單的玩耍 定義 XiaoTianDog 繼承自 Dog,而且重寫 game 方法:哮天犬須要在天上玩耍 定義 Person 類,而且封裝一個 和狗玩 的方法:在方法內部,直接讓 狗對象 調用 game 方法

多態示例
Person 類中只須要讓 狗對象 調用 game 方法,而不關心具體是 什麼狗。

4. 類屬性和類方法

4.1 類的結構

一般會把: 建立出來的 對象 叫作 類的實例 建立對象的 動做 叫作 實例化 對象的屬性 叫作 實例屬性 對象調用的方法 叫作 實例方法 每個對象 都有本身獨立的內存空間,保存各自不一樣的屬性 多個對象的方法,在內存中只有一份,在調用方法時,須要把對象的引用傳遞到方法內部

各個不一樣的屬性,獨一份的方法

在 Python 中,類是一個特殊的對象。

Python 中 一切皆對象:

  • class AAA: 定義的類屬於 類對象
  • obj1 = AAA() 屬於 實例對象

在程序運行時,類一樣會被加載到內存 在程序運行時,類對象在內存中只有一份,使用 一個類能夠建立出不少個對象實例 除了封裝實例的屬性和方法外,類對象還能夠擁有本身的屬性和方法——類屬性、類方法,經過 類名. 的方式能夠 訪問類的屬性 或者 調用類的方法

類的結構

4.2 類屬性和實例屬性

類屬性 就是 類對象中定義的屬性 一般用來記錄與這個類相關的特徵 類屬性不會用於記錄具體對象的特徵 示例: 定義一個 工具類,每件工具都有本身的 name需求 —— 知道使用這個類,建立了多少個工具對象

class Tool(object):

    # 使用賦值語句,定義類屬性,記錄建立工具對象的總數
    count = 0

    def __init__(self, name):
        self.name = name

        # 針對類屬性作一個計數+1
        Tool.count += 1


# 建立工具對象
tool1 = Tool("斧頭")
tool2 = Tool("榔頭")
tool3 = Tool("鐵鍬")

# 知道使用 Tool 類到底建立了多少個對象?
print("如今建立了 %d 個工具" % Tool.count)
複製代碼
屬性的獲取機制

在 Python 中 屬性的獲取 存在一個 向上查找機制

所以,要訪問類屬性有兩種方式:

  • 類名.類屬性
  • 對象.類屬性 (不推薦,由於若是使用 對象.類屬性 = 值 賦值語句,只會給對象添加一個屬性,而不會影響到類屬性的值

4.3 類方法和靜態方法

4.3.1 類方法

  • 類屬性 就是針對 類對象 定義的屬性
    • 使用 賦值語句 在 class 關鍵字下方能夠定義 類屬性
    • 類屬性 用於記錄 與這個類相關 的特徵
  • 類方法 就是針對 類對象 定義的方法
    • 類方法 內部能夠直接訪問 類屬性 或者調用其餘的 類方法

語法以下

@classmethod
def 類方法名(cls):
    pass
複製代碼
  • 類方法須要用 修飾器 @classmethod 來標識,告訴解釋器這是一個類方法
  • 類方法的 第一個參數 應該是 cls
    • 哪個類 調用的方法,方法內的 cls 就是 哪個類的引用
    • 這個參數和 實例方法 的第一個參數是 self 相似
    • 提示 使用其餘名稱也能夠,不過習慣使用 cls
  • 經過 類名. 調用 類方法調用方法時,不須要傳遞 cls 參數
  • 在方法內部
    • 能夠經過 cls. 訪問類的屬性
    • 也能夠經過 cls. 調用其餘的類方法

示例

  • 定義一個 工具類,每件工具都有本身的 name
  • 需求 —— 在 封裝一個 show_tool_count 的類方法,輸出使用當前這個類,建立的對象個數
@classmethod
def show_tool_count(cls):
    """顯示工具對象的總數"""
    print("工具對象的總數 %d" % cls.count)

複製代碼

4.3.2 靜態方法

  • 在開發時,若是須要在 中封裝一個方法,這個方法:
    • 不須要 訪問 實例屬性 或者調用 實例方法
    • 不須要 訪問 類屬性 或者調用 類方法
  • 這個時候,能夠把這個方法封裝成一個 靜態方法

語法以下

@staticmethod
def 靜態方法名():
    pass
複製代碼
  • 靜態方法 須要用 修飾器 @staticmethod 來標識,告訴解釋器這是一個靜態方法
  • 經過 類名. 調用 靜態方法

示例

  • 靜態方法 show_help 顯示遊戲幫助信息
  • 類方法 show_top_score 顯示歷史最高分
  • 實例方法 start_game 開始當前玩家的遊戲
class Game(object):

    # 遊戲最高分,類屬性
    top_score = 0

 @staticmethod
    def show_help():
        print("幫助信息:讓殭屍走進房間")
        
 @classmethod
    def show_top_score(cls):
        print("遊戲最高分是 %d" % cls.top_score)

    def __init__(self, player_name):
        self.player_name = player_name

    def start_game(self):
        print("[%s] 開始遊戲..." % self.player_name)
        
        # 使用類名.修改歷史最高分
        Game.top_score = 999

# 1. 查看遊戲幫助
Game.show_help()

# 2. 查看遊戲最高分
Game.show_top_score()

# 3. 建立遊戲對象,開始遊戲
game = Game("小明")

game.start_game()

# 4. 遊戲結束,查看遊戲最高分
Game.show_top_score()

複製代碼

探索

  • 實例方法 —— 方法內部須要訪問 實例屬性
    • 實例方法 內部可使用 類名. 訪問類屬性
  • 類方法 —— 方法內部 只須要訪問 類屬性
  • 靜態方法 —— 方法內部,不須要訪問 實例屬性 和 類屬性

5. 單例

5.1 單例設計模式

  • 設計模式

    • 設計模式前人工做的總結和提煉,一般,被人們普遍流傳的設計模式都是針對 某一特定問題 的成熟的解決方案
    • 使用 設計模式 是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性
  • 單例設計模式

    • 目的 —— 讓 建立的對象,在系統中 只有 惟一的一個實例
    • 每一次執行 類名() 返回的對象,內存地址是相同的
  • 單例設計模式的應用場景

    • 音樂播放 對象
    • 回收站 對象
    • 打印機 對象
    • ……

5.2 靜態方法: __new__

  • 使用 類名() 建立對象時,Python 的解釋器 首先 會 調用 __new__ 方法爲對象 分配空間
  • __new__ 是一個 由 object 基類提供的 內置的靜態方法,主要做用有兩個:
      1. 在內存中爲對象 分配空間
      1. 返回 對象的引用
  • Python 的解釋器得到對象的 引用 後,將引用做爲 第一個參數,傳遞給 __init__ 方法

重寫 __new__ 方法 的代碼很是固定!

  • 重寫 __new__ 方法 必定要 return super().__new__(cls),不然 Python 的解釋器 得不到 分配了空間的 對象引用就不會調用對象的初始化方法
  • 注意:__new__ 是一個靜態方法,在調用時須要 主動傳遞 cls 參數

class MusicPlayer(object):

    def __new__(cls, *args, **kwargs):
        # 若是不返回任何結果,就不會調用對象的初始化方法
        return super().__new__(cls)

    def __init__(self):
        print("初始化音樂播放對象")

player = MusicPlayer()

print(player)

複製代碼

5.3 Python 中的單例

  • 單例 —— 讓 建立的對象,在系統中 只有 惟一的一個實例
    1. 定義一個 類屬性,初始值是 None,用於記錄 單例對象的引用
    2. 重寫 __new__ 方法
    3. 若是 類屬性 is None,調用父類方法分配空間,並在類屬性中記錄結果
    4. 返回 類屬性 中記錄的 對象引用

class MusicPlayer(object):

    # 定義類屬性記錄單例對象引用
    instance = None

    def __new__(cls, *args, **kwargs):

        # 1\. 判斷類屬性是否已經被賦值
        if cls.instance is None:
            cls.instance = super().__new__(cls)

        # 2\. 返回類屬性的單例引用
        return cls.instance

複製代碼

只執行一次初始化工做

  • 在每次使用 類名() 建立對象時,Python 的解釋器都會自動調用兩個方法:
    • __new__ 分配空間
    • __init__ 對象初始化
  • 在對 __new__ 方法改造以後,每次都會獲得 第一次被建立對象的引用
  • 可是:初始化方法還會被再次調用

需求

  • 初始化動做 只被 執行一次

解決辦法

  1. 定義一個類屬性 init_flag 標記是否 執行過初始化動做,初始值爲 False
  2. __init__ 方法中,判斷 init_flag,若是爲 False 就執行初始化動做
  3. 而後將 init_flag 設置爲 True
  4. 這樣,再次 自動 調用 __init__ 方法時,初始化動做就不會被再次執行
class MusicPlayer(object):

    # 記錄第一個被建立對象的引用
    instance = None
    # 記錄是否執行過初始化動做
    init_flag = False

    def __new__(cls, *args, **kwargs):

        # 1\. 判斷類屬性是不是空對象
        if cls.instance is None:
            # 2\. 調用父類的方法,爲第一個對象分配空間
            cls.instance = super().__new__(cls)

        # 3\. 返回類屬性保存的對象引用
        return cls.instance

    def __init__(self):

        if not MusicPlayer.init_flag:
            print("初始化音樂播放器")

            MusicPlayer.init_flag = True

# 建立多個對象
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

複製代碼

Tips

一、Python 可以自動的將一對括號內部的代碼鏈接在一塊兒:

return ("戶型:%s\n總面積:%.2f[剩餘:%.2f]\n傢俱:%s"
                % (self.house_type, self.area,
                   self.free_area, self.item_list))
複製代碼

二、一個對象的 屬性 能夠是 另一個類建立的對象。 三、在__init__方法中定義類的屬性時,若是 不知道設置什麼初始值,能夠設置爲 None):None 關鍵字 表示 什麼都沒有,表示一個 空對象,沒有方法和屬性,是一個特殊的常量。能夠將 None 賦值給任何一個變量

在 Python 中針對 None 比較時,建議使用is 判斷

四、eval() 函數十分強大 —— 將字符串 當成 有效的表達式 來求值 並 返回計算結果

# 基本的數學計算
In [1]: eval("1 + 1")
Out[1]: 2

# 字符串重複
In [2]: eval("'*' * 10")
Out[2]: '**********'

# 將字符串轉換成列表
In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list

# 將字符串轉換成字典
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}"))
Out[4]: dict
複製代碼

在開發時千萬不要使用 eval 直接轉換 input 的結果,舉個例子:

__import__('os').system('ls')

# 等價代碼

import os

os.system("終端命令")

複製代碼
參考教程: 1.【易百python教程】https://www.yiibai.com/python
相關文章
相關標籤/搜索