1. 程式人生 > >人生苦短我用Python,本文助你快速入門

人生苦短我用Python,本文助你快速入門

[TOC] > 友情提示:本文針對的是非程式設計零基礎的朋友,可以幫助我們快速瞭解Python語法,接著就可以快樂的投入到實戰環節了。如果是零基礎,還是老老實實看書最為穩妥。 ## 前言 ​ 偶然在知乎上看到了一些好玩的Python專案([學 Python 都用來幹嘛的?](https://www.zhihu.com/question/34098079)),讓我對Python產生了些許興趣。距離北漂實習還有兩個月時間,正好可以在這段空閒時間裡學一學。如果能做出些小工具,說不定對工作還有幫助,何樂而不為呢? ​ 關於環境的安裝和IDE就不多說了,網上有很多教程。這裡貼出一篇部落格,大家按裡面的步驟安裝就行:[VSCode搭建Python開發環境](https://blog.csdn.net/qq429477872/article/details/101721869)。使用VSCode主要是因為免費,而且有大量外掛可以下載,大家可以盡情的定製自己的IDE。如果曾經沒有使用過VSCode,最好多瞭解下哪些必須的外掛,優化自己的Coding體驗。比如:[Python外掛推薦](https://blog.csdn.net/hnshhshjq/article/details/80140401)。 ​ 環境搭建好後,就可以愉快地敲程式碼了。VSCode需要自己建立Python檔案,以.py為字尾。Ctrl+F5執行程式,F5除錯程式。 ## Python基礎 ### 註釋 ​ 單行註釋:# ​ 多行註釋:''' (三個英文單引號開頭,三個英文單引號結尾) ```python # 這是單行註釋 ''' 這是多行註釋 ''' ``` ### 變數 ​ Python的變數定義不需要顯式指明資料型別,直接【變數名=值】即可。注意變數名分大小寫,如Name和name不是同一個變數。 ```python name = "小王" print(name) # 輸出 小王 ``` ### 資料型別 ​ Python提供**6種基礎的資料型別**:數字型別(number)、字串型別(string)、列表(list)、元組(tuple)、字典(dictionary)、集合(set)。其中數字型別還包括三種數值型別:整型(int)、浮點型(float)、複數型別(complex)。 ​ 列表、元組那些我們留在容器那一節裡面講,先看看數字型別。 #### 浮點型 ​ 浮點型表示小數,我們建立一個浮點型變數,再通過type函式看一看它的型別: ```python pi = 3.1415926 print(type(pi)) # 輸出 ``` ​ **int整數型**就不說了,其為Integer的縮寫。 #### 複數型別 ​ 複數型別,所謂複數就是我們中學學的,實數+虛數,比如: ```python x = 10+1.2j # 虛數以j或J結尾 print(type(x)) # 輸出 ``` ​ 剛開始接觸複數時,很納悶為啥會有這種型別,到底有啥實際作用,遂百度了一番: >mzy0324:微電子方面的運算基本全部都是複數運算。 > >hilevel:至少複數用來計算向量的旋轉要比矩陣方便多了。科學計算和物理應該會用得到吧。PS:我經常把Python當帶程式設計功能的計算器用,用來除錯純粹的數學演算法挺方便的。 > >morris88:Python 的一大應用領域,主要是科學計算,主要用於太空宇航、銀行等。 ​ 聯想到Python平時在演算法、科學研究等領域應用頗多,所以也就明白了,只是自己沒使用的需求而已。 #### 字串 ​ 字串型別的變數定義用一對**雙引號或者單引號**括起來。如: ```python x = "Hello Python" y = 'Hello Python' print(x,y) # 輸出Hello Python Hello Python ``` ​ 字串內建函式: | 函式 | 作用 | | :---------------------------: | :----------------------------------------------------------: | | find(str[,start,end]) | 在字串中查詢子串str,可選引數start和end可以限定範圍 | | count(str[,start,end]) | 在字串中統計子串str的個數,可選引數start和end可以限定範圍 | | replace(old,new[,count]) | 在字串中用new子串替換old子串,可選引數count代表替換個數,預設全部替換 | | split(sep[,maxsplit]) | 用指定分隔符sep分割字元,返回一個列表,可選引數maxsplit代表分割幾次,預設全部 | | upper()、lower() | 轉換大小寫 | | join(序列) | 把序列中的元素用指定字元隔開並生成一個字串。 | | startwith(prefix[,start,end]) | 判斷字串中是否以prefix開頭,返回bool型別。還有一個**endwith**,判斷結尾的。 | | strip([,str]) | 去掉字串開頭和結尾的空白字元(包括\n、\t這些),可選引數代表可以去掉指定字元 | #### 布林型別 ​ 順便再說一下布林型別,不過與Java不同的是,布林型別的True和False,首字母必須大寫: ```python x = True print(type(x)) # 輸出 ``` ### 型別轉換 ​ 說完幾個基本的資料型別,不免要提到型別轉換。Python內建一些型別轉換的函式: | 函式名 | 作用 | | :------: | :---------------------------------------: | | int(x) | 將x轉換為整型(小數轉整型會去掉小數部分) | | float(x) | 將x轉換為浮點型 | | str(x) | 將x轉換為字串 | | tuple(x) | 將x轉換為元組 | | list(x) | 將x轉換為列表 | | set(x) | 將x轉換為集合,並去重 | ### 輸入與輸出 ​ 輸入函式為**input**。input函式返回使用者輸入的資訊為字串型別。所以如果你輸入的是數字型別,**記得型別轉換**。 ```python x = input("請輸入數字") print(type(x),x) # 輸出 10 ``` ​ 輸出前面已經演示了很多次了,函式為**print**,可以直接輸出變數與值。一次輸出多個變數可以用**逗號隔開**,就想上面的演示一樣,既要輸出型別,也要輸出值。**不換行**輸出,可以在print函式里加上end=""這個引數,因為print預設end="\n",\n就是換行的意思。如果想輸出特殊字元,可能需要用到轉義字元:\。 ```python x = 10 y = 20 print(x,y,end="") # 輸出10 20 加上end="" 不換行 print("Hello \\n Python") # 輸出 Hello \n Python ``` ​ 在輸出時,還可以**格式化**輸出內容:%s代表字串格式、%d代表整型、%f代表浮點型 ```python z = 1.2 print("%f"%z) # 輸出 1.200000 ``` ​ 除了格式化,%d等還可以當作佔位符: ```python name = "小明" age = 18 print("姓名:%s,年齡:%d"%(name,age)) # 姓名:小明,年齡:18 ``` ​ 如果你閒這個佔位符麻煩,還可以使用format函式,佔位符只用寫一對{}: ```python print("姓名:{},年齡:{}".format(name,age)) # 姓名:小明,年齡:18 ``` ### 運算子 #### 算術運算子 ​ 除了加減乘除,還有冪(**)、取模(%)、取整(//) ```python x = 3 ** 2 # x=9 即3的2次方 y = 5 % 3 # y=2 即5除以3餘2 z = 5 // 2 # z=2 即5除以2,整數部分為2 ``` #### 比較運算子 ​ 和其他常用程式語言基本一模一樣,不等於(!=)、大於等於(>=)、等於(==)。 #### 賦值運算子 ​ Python也支援+=、*=等形式的賦值運算。除此之外,當然也支援前面說到的冪、取模等算術運算子,如取整並賦值(//=)、取模並賦值(%=)。 ```python x = 10 x %= 3 print(x) # 輸出1 ,x%=3 意為 x = x%3 ``` #### 邏輯運算子 ​ 非(not)、與(and)、或(or) ```python x = True print(not x) # 輸出 False ``` ### if、while、for ​ 這三個和其他程式語言基本沒差,就是寫法上有點區別。首先沒了大括號,條件語句後以**冒號**開頭;程式碼快有**嚴格的縮排要求**,因為沒了大括號,縮排就是條件語句判斷自己程式碼快範圍的依據。其他的基本一樣,比如continue跳過當次迴圈,break跳出整個迴圈體。下面看三個簡單的例子就明白了: ```python a = 10 # if或else後面是冒號,程式碼塊還需要縮排 if a >= 10: print("你好啊老大") else: print("滾蛋") # 同樣的while後面也需要冒號,程式碼塊必須縮排。(Python沒有num++,得寫成num+=1) # print想不換行列印,最後得加個end="",因為預設有一個end="\n" # " "*(j-i),代表j-i個空格 i = 1 j = 4 while i <= j: print(" "*(j-i), end="") n = 1 while n <= 2*i-1: print("*", end="") n += 1 print("") i += 1 # 語法:for 變數 in 序列 ,還沒講序列,暫時用range表示,代表1-21的序列 # continue略過當次迴圈,break跳出整個迴圈 for i in range(1, 21): if i % 2 == 0: if(i % 10 == 0): continue if(i >= 15): break print(i) ``` ## 容器 ### 列表 ​ 列表使用一對[]定義,每個元素用逗號隔開,元素型別不強求相同,通過索引獲取列表元素。具體的我們看下面的程式碼: ```python info_list = ["小紅", 18, "男"] #可以不是同一型別 info_list[2] = "女" # 修改指定索引位置的元素 del info_list[1] # 刪除指定索引位置的元素 info_list.remove("女") # 刪除列表中指定的值 for att in info_list: # 遍歷元素 print(att) ``` ​ 上面的示例程式碼演示了部分列表的用法,下面再列出一些其他的常用函式或語法: | 函式或語法 | 作用 | | :------------------------: | :-----------------------------------------------: | | list.append(element) | 向列表list結尾新增元素(這個元素也可以是個列表) | | list.insert(index,element) | 向列表指定位置新增元素 | | list.extend(new_list) | 向列表list新增new_list的所有元素 | | list.pop([,index]) | 彈出最後一個元素,可選引數index,彈出指定位置元素 | | list.sort([,reverse=True]) | 對列表排序,可選引數reverse=True表示降序 | | list[start:end] | 對列表分片,start和end代表起始結束索引 | | list1+list2 | 拼接兩個列表 | ### 元組 ​ 元組用一對()定義。元組也是有序的,它和列表的區別就是,列表可以修改元素,元組不行。正是因為這個特點,元組佔用的記憶體也比列表小。 ```python name_list=("小紅","小王") ``` ### 字典 ​ 字典使用一對{}定義,元素是鍵值對。用法示例如下: ```python user_info_dict = {"name": "小王", "age": "18", "gender": "男"} name = user_info_dict["name"] # 直接用key獲取value age = user_info_dict.get("age") # 也可以用get(key)獲取value user_info_dict["tel"] = "13866663333" # 當key不存在,就是往字典新增鍵值對,如果存在就是修改value del user_info_dict["tel"] # 刪除指定鍵值對 ``` ​ 以上就是常用語法和函式。字典也可以遍歷,只是遍歷時,需要指定遍歷的是key還是value,比如: ```python for k in dict.keys(): # 遍歷所有key for v in dict.values(): # 遍歷所有value for item in dict.items(): # 也可以直接遍歷鍵值對 ``` ### 集合 ​ 集合是無序的,也用一對{}定義,但不是鍵值對了,是單獨且不重複的元素。部分用法如下: ```python user_id_set = {"1111","22222","3333"} # 元素不重複 print(type(user_id_set)) # 輸出 # 除了直接用{}定義,還可以用set函式傳入一個序列,其會為list去重,並返回一個集合(如果是字串,字串會被拆成字元) new_user_id_set = set(list) ``` ​ 上面演示了部分用法,下面我們用一個表格展示一些常用的函式或語法: | 函式或語法 | 作用 | | :----------------------------------: | :----------------------------------------------------------: | | element in set | 判斷元素是否在集合中,返回布林型別 | | element not in set | 判斷元素是否不在集合中 | | set.add(element) | 向集合新增元素 | | set.update(list,.....) | 將序列中的每個元素去重並新增到集合中,如果有多個序列,用逗號隔開 | | set.remove(element) | 刪除指定元素,如果元素不存在就會報錯 | | set.discard(element) | 刪除指定元素,如果元素不存在也不會報錯 | | set.pop() | 隨機刪除集合中的元素,並返回被刪除的元素 | | set1 & set2 或set1 intersection set2 | 求兩個集合的交集,兩種用法結果一樣 | | set1 \| set2 或set1 union set2 | 求兩個集合的並集 | | set1 - set2 或set1.difference(set2) | 求兩個集合的差集,注意順序。set1-set2代表set1有set2沒有的元素 | ## 函式 ### 函式的定義 ​ Python中函式用**def**定義,格式為: ```python def function_name(引數列表): # 引數可為空,多個引數用逗號隔開 函式體 return 返回值 #可選 # 函式的呼叫 function_name(引數列表) ``` ### 預設引數 ​ 和迴圈體一樣的,因為沒有了大括號,所以**縮排是嚴格要求的**。除了上面那種比較常見的格式,Python函式的引數中,還有一種預設引數,即**帶有預設值的引數**。呼叫帶有預設引數的函式時,可以不用傳入預設引數的值,如果傳入了預設引數的值,則會使用傳入的值。 ```python def num_add(x,y=10): # y為預設函式,如果呼叫這個函式只傳入了x的值,那麼y預設為10 ``` ### 命名引數 ​ 一般情況下,呼叫函式傳入實參時,都會遵循引數列表的順序。而命名引數的意思就是,呼叫函式時,通過引數名傳入實參,這樣可以不用按照引數定義的順序傳入實參。 ```python def num_add(x, y): print("x:{},y:{}".format(x, y)) return x+y # 輸出: # x:10,y:5 # 15 print(num_add(y=5, x=10)) ``` ### 不定長引數 ​ 不定長引數可以接收任意多個引數,Python中有兩種方法接收:1.在引數前加一個*,傳入的引數會放到元組裡;2.在引數前加兩個**,代表接收的是鍵值對形式的引數。 ```python # 一個* def eachNum(*args): print(type(args)) for num in args: print(num) # 輸出: # ‘ # (1, 2, 3, 4, 5) eachNum(1,2,3,4,5) ## 兩個**。這個other是想告訴你,在使用不定長引數時,也可以搭配普通的引數 def user_info(other,**info): print(type(info)) print("其他資訊:{}".format(other)) for key in info.keys(): print("{} : {}".format(key,info[key])) # 傳入引數時,不用像定義字典一樣,加個大括號再新增鍵值對,直接當命名引數傳入即可 # 輸出: # # 其他資訊:管理員 # 略... user_info("管理員",name="趙四",age=18,gender="男") ``` ​ 上面示例程式碼中的註釋說到了,當使用不定長引數時,不用像字典或者元組的定義那樣,直接傳入引數即可。但有時候,可能會遇到想把字典、元組等容器中的元素傳入到不定長引數的函式中,這個時候就需要用到**拆包**了。 ​ 所謂拆包,其實就是在傳入引數時,在容器前面加上一個或兩個*。還是以上面的user_info函式為例: ```python user_info_dict={"name":"趙四","age":18,"gender":"男"} user_info("管理員",**user_info_dict) # 效果和上面一樣 ``` ​ 注意,如果接收方的不定長引數只用了一個 * 定義,那麼傳入實參時,也只能用一個 *。 ### 匿名函式 ​ 匿名函式,即沒有名字的函式。在定義匿名函式時,既不需要名稱,也不需要def關鍵字。語法如下: ```python lambda 引數列表: 表示式 ``` ​ 多個引數用逗號隔開,匿名函式會自動把表示式的結果return。在使用時,一般會用一個變數接收匿名函式,或者直接把匿名函式當引數傳入。 ```python sum = lambda x,y : x+y print(sum(1,2)) # 輸出3 ``` ### 閉包和裝飾器 ​ 在Python中,函式內還可以定義函式,外面這個函式我們就稱為外部函式,裡面的函式我們就稱為內部函式。而外部函式的返回值是**內部函式的引用**,這種表達方式就是**閉包**。內部函式可以呼叫外部函式的變數,我們看一個示例: ```python # 外部函式 def sum_closure(x): # 內部函式 def sum_inner(y): return x+y return sum_inner # 返回內部函式 # 獲取了內部函式 var1 = sum_closure(1) print(var1) # 輸出.sum_inner at 0x000001D82900E0D0>,是個函式型別 print(var1(2)) # 輸出3 ``` ​ 說完閉包的用法,接著瞭解一下**裝飾器**。不知道大家瞭解過AOP沒,即面向切面程式設計。說人話就是在目標函式前後加上一些公共函式,比如記錄日誌、許可權判斷等。Python中當然也提供了實現切面程式設計的方法,那就是裝飾器。裝飾器和閉包一起,可以很靈活的實現類似功能,下面看示例: ```python import datetime #如果沒有這個包,在終端裡輸入pip3 install datetime # 外部函式,其引數是目標函式 def log(func): #內部函式,引數得和目標函式一致。也可以使用不定長引數,進一步提升程式靈活性 def do(x, y): # 假裝記錄日誌,執行切面函式。(第一次datetime是模組、第二個是類、now是方法。在下一節講到模組) print("時間:{}".format(datetime.datetime.now())) print("記錄日誌") # 執行目標函式 func(x, y) return do # @就是裝飾器的語法糖,log外部函式 @ log def something(x, y): print(x+y) # 呼叫目標函式 # 輸出: # 時間:2021-01-06 16:17:00.677198 # 記錄日誌 # 30 something(10, 20) ``` ​ 函式相關的就說到這裡了,其實還有一些知識沒說到,比如變數的作用域、返回值等。這部分內容和其他語言幾乎無異,一點區別無非就是返回值不用在乎型別了,畢竟定義函式時也沒指定函式返回值型別,這一點各位老司機應該也會想到。 ## 包和模組 ### 包 ​ Python中包與普通資料夾的區別就是,包內要建立一個\_\_init\_\_.py檔案,來標識它是一個包。這個檔案可以是空白的,也可以定義一些**初始化操作**。當其他包下的模組呼叫本包下的模組時,**會自動的執行\_\_init\_\_.py檔案的內容**。 ### 模組 ​ 一個Python檔案就是一個模組,不同包下的模組可以重名,在使用的時候以“包名.模組名”區別。匯入其他模組用import關鍵字,前面的示例程式碼中也演示過一次。匯入多個模組可以用逗號隔開,也可以直接分開寫。除了匯入整個模組,還可以匯入模組中指定的函式或類: ```python from model_name import func_name(or class_name) ``` ​ 匯入函式或類後,就不要使用模組名了,**直接呼叫匯入的類或函式即可**。 ## 面向物件 ### 類和物件 ​ Python是一種面向物件的解釋型程式語言。面向物件的關鍵就在於類和物件。Python中類的定義用class關鍵字,如下: ```python class 類名: def 方法名(self[,引數列表]) ... ``` ​ 定義在類裡面的函式叫做方法,只是與類外部的函式做個區分,不用在意叫法。類裡面的方法,引數列表中會有一個預設的引數,表示當前物件,**你可以當作Java中的this**。因為一個類可以建立多個物件,有了self,Python就知道自己在操作哪個物件了。我們在呼叫這個方法時,**不需要手動傳入self**。示例程式碼: ```python class Demo: def do(self): print(self) # 建立兩個Demmo型別的物件 demo1=Demo() demo1.do() # 輸出<__main__.Demo object at 0x0000019C78106FA0> demo2=Demo() demo2.do() # 輸出<__main__.Demo object at 0x0000019C77FE8640> print(type(demo1)) # ``` ### 構造方法 ​ 構造方法的作用是在建立一個類的物件時,對物件進行初始化操作。Python中類的構造方法的名稱是\_\_init\_\_(兩邊分別兩個下劃線)。在建立物件時,\_\_init\_\_方法自動執行。和普通方法一樣的,如果你想自定義構造方法,也要接收self引數。示例程式碼: ```python class Demo: # 構造方法,還可以傳入其他引數化 def __init__(self,var1,var2): # 把引數設定到當前物件上,即使類中沒有屬性也可以設定 self.var1=var1 self.var2=var2 print("初始化完成") def do(self): print("Working...") # 通過構造方法傳入實參 demo1=Demo(66,77) demo1.do() # 通過當前物件,獲取剛剛設定的引數 print(demo1.var1) print(demo1.var2) ``` ### 訪問許可權 ​ Java或C#中有好幾種訪問許可權,在Python中,**屬性和方法前新增兩個下劃線**即為私有,反之就是共公有。具有私有訪問許可權的屬性和方法,**只能在類的內部方法**,外部無法訪問。和其他語言一樣,私有的目的是為了保證屬性的準確性和安全性,示例程式碼如下: ```python class Demo: # 為了方便理解,我們顯示的設定一個私有屬性 __num = 10 # 公有的操作方法,裡面加上判斷,保證資料的準確性 def do(self, temp): if temp > 10: self.__set(temp) # 私有的設定方法,不讓外部直接設定屬性 def __set(self, temp): self.__num = temp # 公有的get方法 def get(self): print(self.__num) demo1 = Demo() demo1.do(11) demo1.get() # 輸出 11 ``` ​ 一堆self.剛開始看時還有點暈乎,把它當作this就好。 ### 繼承 ​ 繼承是面向物件程式設計裡另一大利器,好處之一就是程式碼重用。子類只能繼承父類的**公有屬性和方法**,Python的語法如下: ```python class SonClass(FatherClass): ``` ​ 當我們建立一個SonClass物件時,直接可以用該物件呼叫FatherClass的公有方法。Python還支援多繼承,如果是多繼承就在小括號裡把父類用逗號隔開。 ​ 如果想在子類裡面呼叫父類的方法,一般有兩種方式:1.父類名.方法名(self[,引數列表])。此時的self是子類的self,且需要顯示傳入;2.super().方法名()。第二種方式因為沒有指定父類,所以在多繼承的情況下,如果呼叫了這些父類中同名的方法,Python實際會執行小括號裡寫在前面的父類中的方法。 ​ 如果子類定義了與父類同名的方法,子類的方法就會覆蓋父類的方法,這就是**重寫**。 ## 異常處理 ### 捕獲異常 ​ 捕獲異常的語法如下: ```python try: 程式碼快 # 可能發生異常的程式碼 except (異常型別,...) as err: # 多個異常型別用逗號隔開,如果只有一個異常型別可以不要小括號。err是取的別名 異常處理 finally: 程式碼快 # 無論如何都會執行 ``` ​ 在try程式碼塊中,錯誤程式碼之後的程式碼是不會執行的,但**不會影響到try ... except之外的程式碼**。看個示例程式碼: ```python try: open("123.txt") #開啟不存在的檔案,發生異常 print("hi") # 這行程式碼不會執行 except FileNotFoundError as err: print("發生異常:{}".format(err)) # 異常處理 print("我是try except之外的程式碼") #正常執行 ``` ​ 雖然上面的內容和其他語言相差不大,但是剛剛接觸Python鬼知道有哪些異常型別,有沒有類似Java的Exception異常型別呢?肯定是有的。Python同樣提供了**Exception**異常型別來捕獲全部異常。 ​ 那如果發生異常的程式碼沒有用try except捕獲呢?這種情況要麼直接報錯,程式停止執行。要麼會被外部的try except捕獲到,也就是說**異常是可以傳遞的**。比如func1發生異常沒有捕獲,func2呼叫了func1並用了try except,那麼func1的異常會被傳遞到func2這裡。是不是和Java的throws差不多? ### 丟擲異常 ​ Python中丟擲異常的關鍵字是**raise**,其作用和Java的throw new差不多。示例程式碼如下: ```python def do(x): if(x>3): # 如果大於3就丟擲異常 raise Exception("不能大於3") # 丟擲異常,如果你知道具體的異常最好,後面的小括號可以寫上異常資訊 else: print(x) try: do(4) except Exception as err: print("發生異常:{}".format(err)) # 輸出 發生異常:不能大於3 ``` ## 檔案操作 ### 讀寫檔案 ​ 想要操作一個檔案,首先得開啟它。Python中有個內建的函式:**open**。使用open開啟檔案可以有三種模式,分別為:只讀(預設的模式,只能讀取檔案內容,r表示)、只寫(會覆蓋原文字內容,w表示)、追加(新內容追加到末尾,a表示)。示例如下: ```python f = open("text.txt","a") # 用追加的方式獲取檔案物件 ``` ​ 因為text.txt和程式碼在同一目錄所以只寫了檔名,如果不在同一目錄需要寫好相對路徑或絕對路徑。 ​ 獲取到檔案物件後,接下來就可以操作了,反正就是些API,直接看示例: ```python f = open("text.txt","a",encoding="utf-8") # 以追加的方式開啟檔案,並設定編碼方式,因為接下來要寫入中文 f.write("234567\n") # 寫入資料,最後的\n是換行符,實現換行 f.writelines(["張三\n","趙四\n","王五\n"]) # write只能寫一個字串,writelines可以寫入一列表的字串 f.close() # 操作完記得關閉 ``` ​ 以上是寫檔案的兩個方法。最後記得關閉檔案,因為作業系統會把寫入的內容快取起來,萬一系統崩潰,寫入的資料就會丟失。雖然程式執行完檔案會自動關閉,但是實際專案中,肯定不止這點程式碼。Python也很貼心,防止我們忘了close,提供了一種安全開啟檔案的方式,語法是 with open() as 別名:,示例如下 ```python with open("test.txt","w") as f: # 安全開啟檔案,不需要close。 f.write("123") ``` ​ 寫完了,該讀一讀了。示例如下: ```python f = open("text.txt","r",encoding="utf-8") data = f.read() # read會一次性讀出所有內容 print(data) f.close() ``` ​ 除了一次性讀取完,還可以按行的方式返回全部內容,並用一個列表裝起來,這樣我們就可以進行遍歷了。方法是readlines,示例如下: ```python f = open("text.txt","r",encoding="utf-8") lines = f.readlines() # lines是個列表 for line in lines: print(line) f.close() ``` ### 檔案管理 ​ 在操作檔案的時候,肯定不止讀寫這麼簡單,可能還會涉及檔案的刪除、重新命名、建立等等。在用Python的函式操作檔案之前,需要匯入os模式:`import os` 。下面簡單的演示一下重新命名的函式,其他的函式我們以表格的形式展現。 ```python import os os.rename("text.txt","123.txt") # 把text.txt改名為123.txt ``` | 函式 | 作用 | | :--------------: | :--------------------------------------------------: | | os.remove(path) | 刪除指定檔案 | | os.mkdir(path) | 在指定路徑下建立新檔案 | | os.getcwd() | 獲取程式執行的絕對路徑 | | os.listdir(path) | 獲取指定路徑下的檔案列表,包含檔案和資料夾 | | os.redir(path) | 刪除指定路徑下的空資料夾(如果不是空資料夾就會報錯) | ### 操作JSON ​ 學了前面的容器,會發現JSON的格式和Python的字典有點像,都是鍵值對形式的。雖然格式很像,但還是有點小區別,比如:Python的元組和列表在JSON中都是列表、Python的True和Flase會被轉換成小寫、空型別None會被轉換成null。下面我們來看一些具體的函式把。 ​ 在Python中操作JSON格式的資料需要匯入json模組。同樣的,我這裡只演示一個函式,其他常用的用表格列出來。 ``` python import json user_info={"name":"張三","age":18,"gender":"男","hobby":("唱歌","跳舞","打籃球"),"other":None} # 建立一個字典 json_str=json.dumps(user_info,ensure_ascii=False) # dumps函式會把字典轉換為json字串 # 輸出 {"name": "張三", "age": 18, "gender": "男", "hobby": ["唱歌", "跳舞", "打籃球"], "other": null} print(json_str) ``` ​ 需要注意如果資料存在中文,需要在dumps函式加上`ensure_ascii=False`。 | 函式 | 作用 | | :-----------------------: | :----------------------------------------------------------: | | json.loads(json_str) | 把json字串轉換為Python資料結構 | | json.dump(user_info,file) | 把Python資料寫入到json檔案,要先獲取檔案,那個file就是檔案物件 | | json.load(file) | 把json檔案中的資料轉為成Python資料結構,同樣需要獲取檔案 | ​ 關於JSON的操作就說這些。通用的資料格式不止JSON一種,比如還有xml、csv等。為了節約篇幅,就不再贅述了,大家可以根據自己的需求查對應的API即可。 ## 正則表示式 ​ 最後一節講正則表示式,一是因為這也算個基礎知識,在很多地方都有可能用到。二是因為後面的爬蟲實戰,肯定會用到正則表示式來解析各種資料。 ​ Python中內建了re模組來處理正常表示式,有了這個模組我們就可以很方便的對字串進行各種規則匹配檢查。不過正則表示式真正難的是表示式的書寫,函式主要就一個:`re.match(pattern,string)`,其中pattren就是正則表示式,stirng就是待匹配字串。如果匹配成功就會返回一個Match物件,否則就返回None。**匹配是從左往右,如果不匹配就直接返回None,不會接著匹配下去**。示例如下: ```python import re res=re.match("asd","asdabcqwe") # 匹配字串中是否有asd(如果asd不在開頭就會返回None) print(res) # 輸出 print(res.group()) # 輸出 asd 如果想獲取匹配的子字元就用這個函式 ``` ​ 秉著幫人幫到底的精神,下面就簡單的介紹下正則表示式的一些規則。 ### 單字元匹配 ​ 單字元匹配,顧名思義就是匹配一個字元。除了直接使用某個具體的字元,還可以使用以下符號來進行匹配: | 符號 | 作用 | | :--: | :----------------------------------------------------------: | | . | 匹配除”\n“以外的任意單個字元 | | \d | 匹配0-9之間的一個數字,等價於[0-9] | | \D | 匹配一個非數字字元,等價於\[^0-9] | | \s | 匹配任意空白字元,如空格、\t、\n等 | | \S | 匹配任意非空白字元 | | \w | 匹配單詞字元,包括字母、數字、下劃線 | | \W | 匹配非單詞字元 | | [] | 匹配[]中列舉的字元,比如[abc],只要出現這三個字母中的一個即可匹配 | ​ 以防有的朋友從未接觸過正則表示式,不知道怎麼用,下面我來做個簡答的演示。假如我想匹配三個字元:第一個是數字、第二個是空格、第三個是字母,一起來看看怎麼寫這個正則表示式吧: ```python import re pattern = "\d\s\w" # \d匹配數字、\s匹配空格、\w匹配字母(切記是從左往右依次匹配的,只要有一個字元匹配不上就直接返回None) string = "2 z你好" res=re.match(pattern,string) print(res.group()) # 輸出:2 z ``` ​ 看到這你可能會想,非得一個個字元匹配,那多麻煩啊,有沒有更靈活的規則?當然有了,接著看。 ### 數量表示 ​ 如果我們只想匹配字母,但不限制有多少個,該怎麼寫呢?看下面的表格就知道了: | 符號 | 作用 | | :---: | :-----------------------------------: | | * | 匹配一個字元出現0次或多次 | | + | 匹配一個字元至少出現一次,等價於{,1} | | ? | 匹配一個字元出現0次或1次,等價於{1,2} | | {m} | 匹配一個字元出現m次 | | {m,} | 匹配一個字元至少出現m次 | | {m,n} | 匹配一個字元出現m到n次 | ​ 數量匹配的符號後面如果加上`?`,**就會盡可能少的去匹配字元**,在Python裡面叫非貪婪模式,反之預設的就是貪婪模式。比如`{m,}`會盡可能多的去匹配字元,而`{m,}?`在滿足至少有m個的情況下儘可能少的去匹配字元。其他的同理。 ​ 來看一個例子,我想匹配開頭是任意個小寫字母,接著是1到5個2-6的數字,最後是至少一個空格: ```python import re pat = r"[a-z]*[2-6]{1,5}\s+" str = "abc423 你好" res=re.match(pat,str) print(res) #輸出 abc423 ``` ​ 我們來解析下這個正則表示式,pat字串開頭的r是告訴Python這是個正則表示式,不要轉義裡面的\,建議寫表示式時都加上。`[a-z]`代表任意小寫字母,不用\w的原因是,\w還包括數字、下劃線,沒有嚴格符合我們的要求。加上個*就代表任意數量。這裡強調一下**單字元匹配和數量表示之間的邏輯關係**,以`[a-z]*`為例,**其表達的是任意個`[a-z]`,而不是某個字母有任意個**。明白了這個邏輯後,其他的也好理解了。 ​ 前面的例子都是我隨意編的,其實學了這些,已經可以寫出一個有實際作用的表示式了,比如我們來匹配一個手機號。首先手機號只有11位,第一個數字必須是1,第二個是3、5、7、8中的一個。知道了這三個個規律,我們來寫一下表達式:`1[3578]\d{9}`。看上去好像可以,但是仔細一想,前面不是說了正則表示式是從左往右匹配,只要符合了就會返回結果,也不會管字串匹配完全沒有。如果最後有10個數字,這個表示式也會匹配成功。關於這個問題我們接著看。 ### 邊界表示 ​ 邊界表示符有兩個:開頭`^`和結尾`$`。使用起來也很簡單,還是以上面的手機號為例,我們再來完善一下:`^1[3578]\d{9}$`。其中`^1`表示以1開頭,`\d{9}$`表示以9個數字結尾。其實這個`^1`可有可無,畢竟是從左往右的,字串不是1開頭的話直接就會返回None,但是這個結尾符是必須的。 ### 轉義字元 ​ 假如我們想匹配的字元與正則表示式規定的這些字元一樣該怎麼辦?比如我們想單純的匹配`.`這個字元,但是這個字元在正則表示式中表示的是任意字元。這時候就要用到轉義字元`\`了。其實這個轉義字元在很多語言裡都是一樣的。那麼前面的例子就可以寫出`\.`。我們再演示個匹配郵箱的例子: ```python import re pat = r"^\w{4,10}@qq\.com" # 如果.前面不加\,就代表任意字元了 str = "[email protected]" res=re.match(pat,str) print(res) ``` ### 匹配分組 ​ 看到上面的匹配郵箱例子,是不是有個疑問,如果我想不止匹配QQ郵箱該怎麼辦呢。那就要用到分組了,其可以實現匹配多種情況。分組符號如下: | 符號 | 作用 | | :-----------: | :-------------------------------------------------------: | | () | 將括號裡的內容當作一個分組,每個分組會有一個編號,從1開始 | | \| | 連線多個表示式,表示式之間是“或”的關係,可與()一起使用 | | \num | 引用分組,num代表分組編號 | | (?P...) | 給分組取別名,別名寫在表示式前面,name不用打引號 | | (?P=name) | 根據別名使用分組中的正則表示式 | ​ 那麼我們把上面的例子稍微修改下:`^\w{4,10}@(qq|163|outlook|gmail)\.com`。這樣就可以匹配多種郵箱了。 ​ 簡單的演示了下`|`的用法,大家可能對其他的分組符號還有點疑惑,下面我們再來演示一下這些符號: ```python import re pat = r"<(.+)><(.+)>.*<(/\2)>
<(/\1)>" str = "" res=re.match(pat,str) print(res) ``` ​ 這個表示式匹配的是由兩個標籤組成的html字串。第一眼看上去有點麻煩,實際很簡單。再次強調一下,普通字元也可以當表示式來匹配的,比如上面的`< >`就是普通字元而已。 ​ 我們來分析一下這個表示式,首先一對小括號表示一個分組,裡面的`.+`表示只有一個非\n字元。中間的`.*`用來匹配標籤內的內容。`/\2`中,第一個斜槓與前面的html標籤組成一對,/2表示引用第二個分組的內容。這裡為什麼要使用分組呢?**因為我們還要保證html標籤正確匹配**。如果後面也使用`.+`,大家可以試著把`/div`和`/body`交換位置,表示式依舊匹配成功,但這顯然不符合html的語法。 ### 操作函式 ​ 正則表示式的一些規則符號終於講完了,最後再列舉幾個Python中操作正則表示式的函式:(re為匯入的模組) | 函式 | 作用 | | :---------------------: | :----------------------------------------------------------: | | re.compile(patt) | 封裝正則表示式,並返回一個表示式物件 | | re.search(patt,str) | 從左往右搜尋第一個配正則表示式匹配的子字串 | | re.findall(patt,str) | 在字串中查詢正則表示式匹配到的所有子字串,並返回一個列表 | | re.finditer(patt,str) | 在字串中查詢正則表示式匹配到的所有子字串,並返回一個Iterator物件 | | re.sub(patt,newstr,str) | 將字串中被正則表示式匹配到的子字串替換成newstr,並返回新的字串,原字串不變 | ​ Python的第一篇文章就到這裡了。接下來會邊學邊寫,做一些好玩的Python專案,再一起分享出來。如有錯誤,感謝指出! > 參考資料:《Python 3快速入門與