2020年9月9日星期三

002 零基礎學的Python 第一篇 Python語言基礎 第2章 Python必須知道的基礎語法

申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!




第一篇 Python語言基礎


第2章 Python必須知道的基礎語法


        Python的語法非常簡練,因此用Python編寫的程式可讀性強、容易理解。本章將向讀者介紹Python的基本語法及其概念,並將其與目前流行的開發語言進行比較。Python的語法與其他高階語言有很多不同,Python使用了許多標記作為語法的一部分,例如空格縮進、冒號等。
        本章的知識點:
        ·Python文件的副檔名
        ·Python的編碼規則
        ·資料類型
        ·變數和常量的定義和使用
        ·運算子及運算式

2.1 Python的檔案類型

        Python檔案類型分為3種,分別是原始程式碼、位元組代碼、優化代碼。這些代碼可以直接運行,不需要進行編譯或者連結。這正是Python這門語言的特性,Python的檔通過Python解譯器解釋運行。Windows中有python.exe與pythonw.exe,一般安裝在路徑C:\Python33 中,當然也可以改變它的路徑,只要保證環境變數設置正確即可。在*nix系統中,Python解譯器被安裝在目的機器的/usr/local/bin/python目錄下,將/usr/local/bin路徑放進shell的搜索路徑中,即可通過python命令在終端調用。
2.1.1 原始程式碼
        Python原始程式碼的副檔名以py結尾,可在控制台下運行。Python語言寫的程式不需要編譯成二進位碼,可以直接運行原始程式碼。pyw是Windows下開發圖形使用者介面(Graphical user interface)的原始檔案,作為桌面應用程式的尾碼名。這種檔是專門用於開發圖形介面的,由pythonw.exe解釋運行。以py和pyw為尾碼名的檔可以用文本工具打開,並修改檔的內容。
2.1.2 位元組代碼
        Python原始檔案編譯後生成pyc尾碼的檔,pyc是編譯過的位元組檔,這種檔不能使用文本編輯工具打開或修改。pyc檔是與平臺無關的,因此Python的程式可以運行在Windows、UNIX、Linux等作業系統上。py檔直接運行後即可得到pyc類型的檔,或通過腳本生成該類型的檔。下面這段腳本可以把hello.py編譯為hello.pyc。 

import py_compile

py_compile.compile('hello.py')


        保存此腳本,運行後即可得到hello.pyc文件。
2.1.3 優化代碼
        副檔名為pyo的文件是優化過的原始檔案,pyo類型的檔需要用命令列工具生成。pyo檔也不能使用文本編輯工具打開或修改。下麵把hello.py編譯成hello.pyo。
       (1)啟動命令列視窗,進入hello.py檔所在的目錄。例如:

cd /D D:\developer\python\example\02\2.1


        D:\developer\python\example\02\2.1是筆者設置的hello.py檔所在的目錄,讀者可根據自己的環境進行修改。
      (2)在命令列中輸入python-O-m py_compile hello.py,並按回車鍵。      

python -O –m py_compile hello.py


     【代碼說明】
        ·參數“-O”表示生成優化代碼。
        ·參數“-m”表示把導入的py_compile模組作為腳本運行。編譯hello.pyo需要調用py_compile模組的compile()方法。
        ·參數“hello.py”是待編譯的檔案名。
        最後,查看hello.py檔所在的目錄,此時目錄中生成了一個名為hello.pyo的文件。

2.2 Python的編碼規範

          Python語言有自己獨特的編碼規則,包括命名規則、代碼書寫規則等。本節將詳細介紹Python中常用的規則,並解釋這些規則的原理和由來。
2.2.1 命名規則
        Python語言有一套自己的命名規則,使用者也可以借鑒Java語言的命名規則形成自己命名的規則。命名規則並不是規定,只是一種習慣用法。下面介紹幾個常見規範。
        1.變數名、包名、模組名
        變數名、包名、模組名通常採用小寫,可使用底線,示例如下。    

01    # 變數、模組名的命名規則

02      # Filename: ruleModule.py

03     

04      _rule = "rule information"


      【代碼說明】
       ·第2行代碼聲明模組的名稱,模組名採用小寫。也可以不指定模組名,以py尾碼的檔就是一個模組。模組名就是檔案名。
      ·第4行代碼定義了一個全域變數_rule。
        2.類名、對象名
        類名首字母採用大寫,物件名採用小寫。類的屬性和方法名以物件作為首碼。類的私有變數、私有方法以兩個底線作為首碼。下面這段代碼演示了類的定義和產生實體的規範寫法。

01       class Student:                         # 類名大寫

02           __name = ""                        # 私有執行個體變數前必須有兩個底線

03           def __init__(self, name):

04               self.__name = name             # self相當於Java中的this

05           def getName(self):                 # 方法名首字母小寫,其後每個單詞的首字母大寫

06               return self.__name

07     

08      if __name__ == "__main__":

09           student = Student("borphi")        # 對象名小寫

10           print(student.getName())


          【代碼說明】
         ·第1行代碼定義了一個名為Student的類,類名首字母大寫。
         ·第2行代碼定義了一個私有的執行個體變數,變數名前有兩個底線 _ _。
         ·第4行代碼使用self首碼說明__name變數屬於Student類。
         ·第5行代碼定義了一個公有的方法,方法名首字母小寫,其後的單詞Name首字母大寫。函數的命名規則和方法名相同。
         ·第9行代碼創建了一個student物件,物件名小寫。
        說明 關於物件導向的知識會在第9章詳細介紹,這裡讀者只需要知道類、物件、屬性以及方法的書寫方式即可。
        3.函數名
        函數名通常採用小寫,並用底線或單詞首字母大寫增加名稱的可讀性,導入的函數以模組名作首碼。下例中,為了演示導入函數首碼寫法,使用了生成亂數的模組random。該模組有一個函數randrange()。該函數可以根據給定的數位範圍生成亂數。randrange()聲明如下所示:       

randrange(start, stop[, step])



         【代碼說明】
         ·參數start表示生成亂數所在範圍的開始數字。
         ·參數stop表示生成亂數所在範圍的結束數位,但不包括數位stop。
        ·參數step表示從start開始往後的步數。生成的亂數在[start,stop-1]的範圍內,取值等於start+step。
         例如:      

randrange(1, 9, 2)



        亂數的範圍在1、3、5、7之間選取。下面這段代碼演示了函數的規範寫法,其中定義了一個compareNum(),該函數用於比較兩個數位的大小,並返回對應的結果。

01      # 函數中的命名規則

02      import random

03     

04      def compareNum(num1, num2):

05          if(num1 > num2):

06              return 1

07          elif(num1 == num2):

08              return 0

09          else:

10              return -1

11      num1 = random.randrange(1, 9)

12      num2 = random.randrange(1, 9)

13      print( "num1 =", num1)

14      print ("num2 =", num2)

15      print (compareNum(num1, num2))


      
       【代碼說明】
         ·第2行代碼導入了random模組。
         ·第4行代碼定義了一個函數compareNum(),參數num1、num2為待比較的兩個變數。
         ·第5行到第10行代碼比較兩個數的大小,返回不同的結果。
         ·第11、12行代碼調用random模組的randrange()函數,返回兩個亂數。
         ·第13、14行代碼輸出亂數,不同的機器、不同的執行時間得到的亂數均不相同。
          ·第15行代碼調用compareNum(),並把產生的兩個亂數作為參數傳入。
        良好命名可以提高程式設計效率,可以使代碼閱讀者在不瞭解文檔的情況下,也能理解代碼的內容。下面以變數的命名為例說明如何定義有價值的名稱。許多程式師對變數的命名帶有隨意性,如使用i、j、k等單個字母。代碼閱讀者並不知道這些變數的真實含義,需要閱讀文檔或仔細查看原始程式碼才能瞭解其含義。下面是一個命名不規範的例子。

01      # 不规范的变量命名

02      sum = 0

03      i = 2000

04      j = 1200

05      sum = i + 12 * j


       【代碼說明】

        這段代碼定義了一個求和變數sum,以及兩個變數i、j。如果只看代碼片段,並不知道運算的含義是什麼,需要通讀整個函數或功能模組才能理解此處運算式的含義。
       下面是一個良好命名的例子。

01      # 规范的变量命名

02      sumPay = 0

03      bonusOfYear = 2000

04      monthPay = 1200

05      sumPay = bonusOfYear + 12 * monthPay


        【代碼說明】

         bonusOfYear表示年終獎金、monthPay表示月薪,因此sumPay表示全年的薪水。命名良好的變數可以節省閱讀程式的時間,更快地理解程式的含義。
        注意 變數的命名應盡可能地表達此變數的作用,儘量避免使用縮寫,以至於任何人都能理解變數名的含義。不用擔心變數名的長度,長的變數名往往能更清楚地表達意思。
        以上討論的命名方式同樣適用於模組名、類名、方法名、屬性名等。命名規則會帶來很多益處。統一命名規則便於開發團隊合作開發同一個項目;便於統一代碼的風格,理解不同程式師編寫的代碼;命名規範的變數名使函數的內容更容易被理解;避免專案中隨意命名變數的情況,促進程式師之間的交流。規則並不是絕對的,統一規則、表達清楚名稱的含義才是制定規則的原因。
2.2.2 代碼縮進與冒號
         代碼縮進是指通過在每行代碼前輸入空格或定位字元的方式,表示每行代碼之間的層次關係。任何程式設計語言都需要用代碼縮進清晰程式的結構,採用代碼縮進的程式設計風格有利於代碼的閱讀和理解。對於C、C++、Java等語言,代碼縮進只是作為程式設計的一種良好習慣而延承下來。對於Python而言,代碼縮進是一種語法,Python語言中沒有採用花括弧或begin…end…分隔代碼塊,而是使用冒號和代碼縮進區分代碼之間的層次。
        使用IDE開發工具或EditPlus等編輯器書寫代碼時,編輯器會自動縮進代碼、補齊冒號,提高編碼效率。下面這段代碼中的條件陳述式採用了代碼縮進的語法。  

01      x = 1

02      if x == 1:

03          print( "x =", x)                    # 代碼縮進

04      else:

05          print( "x =", x)                    # 代碼縮進

06          x = x + 1                           # 代碼縮進

07      print ("x =", x)


        【代碼說明】
        ·第1行代碼創建了變數x,並賦值為1。在設定運算子的兩側各添加一個空格,這是一種良好的書寫習慣,提高了程式的可讀性。
        ·第2行代碼使用了條件陳述式if,判斷x的值是否等於1。if運算式後輸入了一個冒號,冒號後面的代碼塊需要縮進編寫。本行代碼與第1行代碼處於同一個層次,直接從最左端書寫代碼。
        ·第3行代碼表示x的值等於1時輸出的結果。當if條件成立時,程式才能執行到第3行,所以第3行代碼位於第2行代碼的下一個層次。在編碼時,首先在最左端輸入4個空格或製錶鍵(不建議),然後再書寫print語句。輸出結果:    

x = 1


         ·第4行代碼的else關鍵字後是一段新的代碼塊。當x的值不等於1時,程式將執行第5、6行代碼。
       ·第5、6行代碼採用縮進式的代碼風格。
       ·第7行代碼輸出結果:    

x = 1


        Python對代碼縮進要求非常嚴格。如果程式中不採用代碼縮進的編碼風格,將拋出一個IndentationError。下面這段代碼是錯誤的編碼方式。

01      x = 0

02      if x == 1:

03      print( "x =", x)

04      else:

05          print( "x =", x)            # 代码缩进

06          x = x + 1                   # 代码缩进

07      print ("x =", x)


        【代碼說明】

        第3行沒有縮進代碼,python不能識別出代碼的層次關係,python誤認為if x==1:語句後沒有代碼塊。代碼運行後輸出如下錯誤:

IndentationError: expected an indented block


         注意 如果縮進的代碼前只有一個空格或幾個定位字元也是符合語法的,但是這種寫法並不推薦。最佳的方式是編碼前統一代碼的書寫規則,所有代碼前的空格數保持一致,最好使用4個空格縮進。
         每行代碼縮進的情況不同,代碼執行的結果也不同。例如,前文提到的hello.py,else子句包含的兩條語句都採用了代碼縮進的格式,這兩條語句組成了一個代碼塊。如果把hello.py的代碼修改為如下的內容,則變數x的值將有所變化。

01      x = 1

02      if x == 1:

03          print( "x =", x)            # 代碼縮進

04      else:

05          print( "x =", x)            # 代碼縮進

06      x = x + 1

07      print( "x =", x)


      【代碼說明】
        ·第6行代碼與第1行代碼處於同一個層次,是主程序必須執行的代碼,因此變數x的值為2。
        ·第7行代碼的輸出結果如下。

x = 2


        注意 當程式出現問題時,程式師首先要檢查代碼的書寫格式,是否因為代碼縮進的問題導致了不期望的計算結果。
2.2.3 模組導入的規範
        模組是類或函數的集合,用於處理一類問題。模組的導入和Java中包的導入的概念很相似,都使用import語句。在Python中,如果需要在程式中調用標準庫或其他協力廠商庫的類,需要先使用import或from…import…語句導入相關的模組。
        1.import語句
        下面這段代碼使用import語句導入sys模組,並列印相關內容。

01      # 規範導入方式

02      import sys

03     

04      print (sys.path)

05      print (sys.argv)


        【代碼說明】
         ·第2行代碼使用import語句導入了sys模組,sys模組是處理系統環境的函數的集合。
        ·第4行代碼輸出Python環境下的查找路徑的集合,Python預設情況下會查找sys.path返回的目錄清單。清單是Python內置的資料結構,定義了一組相同意義的資料,通常用來作為參數或返回值。關於清單的知識請參考第4章的內容。本行代碼的輸出結果如下。

['D:\\developer\\python\\example\\02\\2.2\\2.2.3','C:\\WINDOWS\\system32\\python25.zip',

'D:\\developer\\python\\DLLs', 'D:\\developer\\python\\lib',

'D:\\developer\\python\\lib\\plat-win',

'D:\\developer\\python\\lib\\lib-tk','D:\\developer\\python',

'D:\\developer\\python\\lib\\site-packages','D:\\developer\\python\\lib\\site-packages\\win32',

'D:\\developer\\python\\lib\\site-packages\\win32\\lib',

'D:\\developer\\python\\lib\\site-packages\\Pythonwin',

'D:\\developer\\python\\lib\\site-packages\\wx-2.8-msw-unicode']


        ·第5行代碼中的sys.argv是存儲輸入參數的清單。預設情況下,argv自帶的參數是檔案名。輸出結果如下。

['import.py']


        2.from…import…語句
        使用from…import…語句導入與使用import語句導入有所不同,區別是前者只導入模組中的一部分內容,並在當前的命名空間中創建導入物件的引用;而後者在當前程式的命名空間中創建導入模組的引用,從而可以使用“sys.path”的方式調用sys模組中的內容。
         下面這段代碼實現了與上面代碼相同的功能,不同的是使用了from…import…語句導入指定內容。

01      #不規範導入方式

02      from sys import path

03      from sys import argv

04     

05      print (path)

06      print (argv)


        【代碼說明】

        第5、6行代碼直接調用path、argv清單的內容,沒有模組名的限定,但這種寫法不夠規範。如果程式比較複雜,導入了很多模組,閱讀程式的人並不瞭解path、argv來自哪個模組。而通過sys.path、sys.argv的寫法可以清楚地知道path、argv來自sys模組。

2.2.4 使用空行分隔代碼

函數之間或類的方法之間用空行分隔,表示一段新的代碼的開始。類和函數入口之間也用一行空行分隔,突出函數入口的開始。下面這段代碼創建了一個類A,類A中定義了兩個方法funX()funY()


01      class A:

02          def funX(self):

03              print( "funY()")

04     

05          def funY(self):

06              print ("funY()")

07     

08      if __name__ == "__main__":

09          a = A()

10          a.funX()

11          a.funY()


【代碼說明】

·第4行代碼插入了一個空行,便於程式師閱讀代碼,表示區分方法之間的間隔。

·第7行代碼也是一個空行。因為下面的if語句是主程序的入口,用於創建類A的物件,並調用其方法。

空行與代碼縮進不同,空行並不是Python語法的一部分。書寫時不插入空行,Python解譯器運行也不會出錯。但是空行的作用在於分隔了兩段不同功能或含義的代碼,便於日後代碼的維護或重構。記住,空行也是程式碼的一部分。

2.2.5 正確的注釋

注釋是用於說明代碼實現的功能、採用的演算法、代碼的編寫者以及代碼創建和修改的時間等資訊。注釋是代碼的一部分,注釋起到了對代碼補充說明的作用。CC++Java均採用///**/作為注釋的標記,Python的注釋方式有所不同。如果只對一行代碼注釋,使用#加若干空格開始,後面是注釋的內容。如果對一段代碼進行注釋,也使用#,段落之間以一個#行分隔。Python會忽略#行的內容,跳過#行執行後面的內容。下面的代碼演示了Python的注釋。


01      # note - show Python's note

02      # Copyright (C) 2008 bigmarten

03      #

04      # This program is free software. you can redistribute it and/or modify

05      # it under the terms of the GNU General Public License as published by

06      # the Free Software Foundation

07      #

08      ########################################################################

09      #

10      # Version is 1.0

11      #

12      # Its contents are calculate payment

13      #

14      ########################################################################

15     

16      # 規範的變數命名

17      sumPay = 0                                   # 年薪

18      bonusOfYear = 2000                           # 年終獎金

19      monthPay = 1200                              # 月薪

20      sumPay = bonusOfYear + 12 * monthPay         # 年薪 = 年終獎金 + 12 * 月薪


【代碼說明】

·第1行代碼對本檔進行摘要說明。

·第2行代碼聲明了版權資訊。

·第3行代碼是空行,說明下面將另起一段,注釋其他的內容。

·第4行到第6行代碼說明程式的許可資訊。

·第8行到第14行代碼說明程式的版本和實現的功能。

·第16行開始的代碼實現程式的功能,並在行末對每行代碼進行單行注釋。

Python還有一些特殊的注釋,完成一些特別的功能,如中文注釋、程式的跨平臺。

1)中文注釋:如果需要在代碼中使用中文注釋,必須在Python檔的最前面加上如下注釋說明。


# -*- coding: UTF-8 -*-


注意  Python3中預設的編碼是Unicode,所以不需在每個Python檔中再加以上注釋,但在Python2中若使用中文則必須加上。

2)跨平臺注釋:如果需要使Python程式運行在*nix系統中,最好在Python檔的最前面加上如下注釋說明。


#!/usr/bin/python


此外,注釋也用於偵錯工具。由於文字編輯器不具備調試功能,因此,可以使用注釋輔助調試。如果一個程式很長,可以把程式分成若干個部分編寫。為了更好地調試目前正在編寫的程式模組,可以將那些已經編譯通過的部分注釋掉;或者把多餘代碼注釋掉,把主要精力集中在當前編寫的邏輯上。

例如,編寫一個比較兩個數位大小的函數。


01      def compareNum(num1, num2):

02          if(num1 > num2):

03              return str(num1)+" > "+str(num2)

04          elif(num1 < num2):

05              return str(num1)+" = "+str(num2)

06          elif(num1 == num2):

07              return str(num1)+" = "+str(num2)

08          else:

09              return ""


【代碼說明】 本段代碼中的str()函數實現了數位類型到字串類型的轉換。

編譯後顯示如下內容:


---------- python ----------

輸出完成 (耗時: 1 ) - 正常終止


說明這個函數編譯通過,至少在語法上沒有任何錯誤。下面就可以編寫這個函數的調用程式,證明這個程式的邏輯是否正確。在前面的程式碼片段後面添加如下代碼。


01      num1 = 2

02      num2 = 1

03      print (compareNum(num1, num2))

04      num1 = 2

05      num2 = 2

06      print (compareNum(num1, num2))

07      num1 = 1

08      num2 = 2

09      print (compareNum(num1, num2))


運行程式,發現第3行的輸出結果有誤。


2 > 1

2 = 2

1 = 2


1行和第2行的輸出證明函數compareNum()num1>num2num1==num2的判斷是正確的,於是想到程式可能是在num1<num2的條件判斷的邏輯上出現了錯誤。為了證明這個觀點,注釋掉num1<num2的分支判斷語句。注釋後的compareNum()的代碼如下。


01       def compareNum(num1, num2):

02          if(num1 > num2):

03              return str(num1)+" > "+str(num2)

04          #elif(num1 < num2):

05          #    return str(num1)+" = "+str(num2)

06          elif(num1 == num2):

07              return str(num1)+" = "+str(num2)

08          else:

09              return ""


此時,程式編譯通過,證明邏輯錯誤就在num1<num2的分支語句中。仔細檢查注釋的語句,發現return語句的返回值運算式寫錯了,其中的>誤寫為=compareNum()正確的寫法如下所示。


01      def compareNum(num1, num2):

02          if(num1 > num2):

03              return str(num1)+" > "+str(num2)

04          elif(num1 < num2):

05              return str(num1)+" < "+str(num2)

06          elif(num1 == num2):

07              return str(num1)+" = "+str(num2)

08          else:

09              return ""


再次運行整個程式,輸出結果正確顯示。


2 > 1

2 = 2

1 < 2


合理地使用注釋可以檢查程式中的錯誤。這段代碼演示了注釋問題語句的做法,並檢查這些語句錯誤的過程。另一種用法是,注釋正確語句,單獨運行問題語句,檢查語句錯誤。這兩種用法可以根據實際情況分別應用,後者更適合於代碼比較長的情況。注釋對於程式非常重要,表2-1說明了注釋的用法和作用。

        表2-1 注釋的用法


2.2.6 語句的分隔

分號是CJava等語言中標識語句結束的標誌。Python也支持分號,同樣可以用分號作為一行語句的結束標識。但在Python中分號的作用已經不像在CJava中那麼重要了,在CJava中分號是必需的;而Python中的分號可以省略,主要通過換行來識別語句的結束。例如,以下兩行代碼是等價的。


01      # 下麵兩條語句是等價的

02      print( "hello world!")

03      print( "hello world!");


【代碼說明】

·第1行代碼的輸出結果:


hello world!


·第2行代碼的輸出結果:


hello world!


如果要在一行中書寫多個語句,就必須使用分號分隔了,否則Python無法識別語句之間的間隔。


01      # 使用分號分隔語句

02      x = 1; y = 1 ; z = 1


【代碼說明】 2行代碼中有3條設定陳述式,語句之間需要用分號隔開。如果不隔開語句,則Python解譯器不能正確解釋,會提示語法錯誤。


SyntaxError: invalid syntax


注意  分號並不是Python推薦使用的符號,Python傾向於使用換行作為每條語句的分隔。簡單直白是Python語法的特點,通常一行只寫一條語句,這樣便於閱讀和理解程式。一行寫多條語句的方式是不好的實踐。

Python同樣支持多行寫一條語句,Python使用\作為分行符號。在實踐中,一條語句寫在多行也是很常見的。例如,把SQL語句作為參數傳遞給函數,由於SQL非常長,因此需要換行書寫,提高閱讀的方便性。


01      # 字串的換行

02      # 寫法一

03      sql = "select id,name \

04      from dept \

05      where name = 'A'"

06      print (sql)

07      # 寫法二

08      sql = "select id,name " \

09            "from dept " \

10            "where name = 'A'"

11      print (sql)


【代碼說明】

·寫法一隻使用了一對雙引號,把SQL語句分為selectfromwhere 3部分分別書寫。

·第6行代碼輸出結果如下。


select id,name from dept where name = 'A'


·寫法二使用了3對雙引號,selectfromwhere分別對應一對雙引號。

·第11行代碼輸出結果如下。


select id,name from dept where name = 'A'


第二種寫法比第一種寫法的可讀性更強,可以使用空格和定位字元對齊語句,使代碼顯得更工整。對於簡短的語句不推薦使用換行的寫法,這種寫法只會造成閱讀的複雜性。下面這段程式是不合理的換行寫法。


01      # 一條語句寫在多行

02      print (\

03      "hello world!")


【代碼說明】 23行代碼是一個整體,調用print輸出hello world!,這種情況不適合分行書寫。

2.3 變數和常量

變數是電腦記憶體中的一塊區域,變數可以存儲任何值,而且值可以改變。常量是一塊唯讀的記憶體區域,常量一旦初始化就不能修改。

2.3.1 變數的命名

變數由字母、數位或底線組成。變數的第1個字元必須是字母或底線,其他字元可以由字母、數位或底線組成。例如:


01      # 正確的變數命名

02      var_1 = 1

03      print (var_1)

04      _var1 = 2

05      print( _var1)


【代碼說明】

·第2行代碼定義了一個名為var_1的變數,該變數的初始值為1。這個變數以字母開頭,後面的字元由字母、底線和數位組成。

·第3行代碼輸出結果如下。


1


·第4行代碼定義了一個名為_var1的變數,該變數的初始值為2。這個變數以底線開頭,後面的字元由字母和數位組成。

·第5行代碼輸出結果如下。


2


下面這段代碼演示了錯誤的變數命名方式。


01      # 錯誤的變數命名

02      1_var = 3

03      print (1_var)

04      $var = 4

05      print ($var)


【代碼說明】

·第2行代碼定義了一個名為1_var的變數,該變數以數位開頭,後面的字元由字母、底線組成。

·第3行代碼,變數以數位開頭,不符合變數命名的規則。提示如下錯誤:


SyntaxError: invalid syntax


·第4行代碼定義了一個名為$var的變數,該變數以$符號開頭。

·第5行代碼,變數以$符號開頭,不符合變數命名的規則。提示如下錯誤:


SyntaxError: invalid syntax


2.3.2 變數的賦值

Python中的變數不需要聲明,變數的賦值操作即是變數聲明和定義的過程。每個變數在記憶體中創建,都包括變數的標識、名稱和資料這些資訊。例如:


x = 1


上面的代碼創建了一個變數x,並且賦值為1,如圖2-1所示。

                                                      圖2-1 變數的內部結構

        Python中一次新的賦值,將創建一個新的變數。即使變數的名稱相同,變數的標識並不相同。下面的代碼演示了Python的變數聲明以及賦值操作。

01      # 一次新的賦值操作,將創建一個新的變數

02      x = 1  

03      print (id(x))

04      x = 2

05      print( id(x))


【代碼說明】

·第2行代碼定義了一個名為x的變數,該變數的初始值為1

·第3行代碼,輸出變數x的標識。輸出結果如下。


11229424


·第4行代碼再次定義了一個x的變數,該變數的初始值為2。該變數與前面的變數x並不是同一變數。

·第5行代碼,輸出變數x的標識。輸出結果如下。


11229412


如果變數沒有賦值,Python將認為該變數不存在。例如:


print y


運行後,解譯器提示:


NameError: name 'y' is not defined


在變數y沒有賦值的前提下,不能直接輸出y的值。每個變數在使用前都必須賦值,這樣可以避免由於變數的空值引起的一些異常。Python支援對一些變數同時賦值的操作,例如:


01      # 給多個變數賦值

02      a = (1, 2, 3)

03      (x, y, z) = a

04      print( "x =", x)

05      print( "y =", y)

06      print( "z =", z)


【代碼說明】

·第2行代碼定義了一個序列a,這個序列有3個值:123

·第3行代碼,把序列a的值分別賦值給序列(x,y,z)中的變數xyz

·第4行代碼輸出變數x的值。輸出結果:


x = 1


·第5行代碼輸出變數y的值。輸出結果:


y = 2


·第6行代碼輸出變數z的值。輸出結果:


z = 3


通過序列的裝包和拆包操作,實現了同時給多個變數賦值。關於序列的概念參見第4章的內容。

2.3.3 區域變數

區域變數是只能在函數或程式碼片段內使用的變數。函數或程式碼片段一旦結束,區域變數的生命週期也就結束。區域變數的作用範圍只在其被創建的函數內有效。例如,檔1fun()中定義了一個區域變數,則該區域變數只能被fun()訪問,而不能被fun2()訪問,也不能被檔2訪問,如圖2-2所示。

下面定義了一個函數fun(),該函數中定義了一個區域變數。


01      # 區域變數

02      def fun():

03          local = 1

04          print(local)

05      fun()


【代碼說明】

·第2行代碼定義了一個函數fun()

·第3行代碼定義了一個區域變數local

·第4行代碼輸出local的值。輸出結果如下。


1


·第5行代碼調用函數fun()。此時已超出local變數的作用範圍。

注意  Python創建的變數就是一個物件,Python會管理變數的生命週期。Python對變數的回收採用的是垃圾回收機制。

2.3.4 全域變數

全域變數是能夠被不同的函數、類或檔共用的變數,在函數之外定義的變數都可以稱為全域變數。全域變數可以被檔內部的任何函數和外部檔訪問。例如,如果檔1中定義了一個全域變數,檔1中的函數fun()可以訪問該全域變數。此外,該全域變數也能被檔1、檔2訪問,如圖2-3所示。

                                                  圖2-2 區域變數的作用範圍

圖2-3 全域變數的作用範圍

        全域變數通常在檔的開始處定義。下面定義了兩個全域變數_a_b和兩個函數add()sub(),這兩個函數將調用全域變數執行加法和減法計算。

01      # 在檔的開頭定義全域變數

02      _a = 1

03      _b = 2

04      def add():

05          global _a

06          _a = 3

07          return "_a + _b =", _a + _b

08      def sub():

09          global  _b

10          _b = 4

11          return "_a - _b =", _a - _b

12      print (add())

13      print( sub())


【代碼說明】

·第2行代碼定義了一個名為_a的全域變數,這個變數的作用範圍從定義處到檔的結尾。之所以使用底線是為了區分於其他變數,引起程式師對全域變數出現的重視。

·第3行代碼定義了一個名為_b的全域變數。同樣,變數_b的作用範圍從定義處到檔的結尾。

·第4行代碼定義了一個函數add(),用於執行加法計算。

·第5行代碼引用全域變數_a。這裡使用了global關鍵字,global用於引用全域變數。

·第6行代碼對全域變數_a重新賦值。

·第7行代碼返回_a+_b的值。

·第8行代碼定義了一個函數sub(),用於執行減法運算。函數內的實現方式和add()相同。

·第12代碼調用函數add()。輸出結果如下。


('_a + _b =', 5)


·第13行代碼調用函數sub()。輸出結果如下。


('_a - _b =', -1)


如果不使用global關鍵字引用全域變數,而直接對_a_b賦值,將得到不正確的結果。


01      # 錯誤地使用全域變數

02      _a = 1

03      _b = 2

04      def add():

05          _a = 3

06          return "_a + _b =", _a + _b

07      def sub():

08          _b = 4

09          return "_a - _b =", _a - _b

10      print (add())

11      print (sub())


【代碼說明】

·第5行代碼中的_a並不是前面定義的全域變數,而是函數add()中的區域變數。雖然輸出的結果相同,但是運算的物件並不相同。

·第6行代碼中的_b還是前面定義的全域變數_b

·第8行代碼中的_b是區域變數。

·第10行代碼的輸出結果如下。


('_a + _b =', 5)


·第11行代碼的輸出結果如下。


('_a - _b =', -3)


注意  變數名相同的兩個變數可能並不是同一個變數,變數的名稱只是起標識的作用。變數出現的位置不同,變數的含義也不同。

同樣可以把全域變數放到一個專門的檔中,便於統一管理和修改。創建一個名為gl.py的文件。


01      # 全域變數

02      _a = 1

03      _b = 2


【代碼說明】 pl.py創建了兩個全域變數_a_b

再創建一個調用全域變數的檔use_global.py


01      # 調用全域變數

02      import gl

03      def fun():

04          print(gl._a)

05          print(gl._b)

06      fun()


【代碼說明】

·第2行代碼導入前面創建的檔gl.py,即模組gl

·第3行代碼定義了一個函數fun(),該函式呼叫全域變數_a_b。這裡不需要使用global引用gl.py中的全域變數,因為前置字元可以定位全域變數_a_b

·第4行代碼輸出_a的值,使用前置字元gl定位。輸出結果:


1


·第5行代碼輸出_b的值,使用前置字元gl定位。輸出結果:


2


·第6行代碼調用fun()

應該儘量避免使用全域變數。因為不同的模組都可以自由地訪問全域變數,可能會導致全域變數的不可預知性。對於gl.py中的全域變數,如果程式師甲修改了_a的值,程式師乙同時也要使用_a,這時可能導致程式中的錯誤。這種錯誤是很難發現和更正的。

全域變數降低了函數或模組之間的通用性,不同的函數或模組都要依賴於全域變數。同樣,全域變數降低了代碼的可讀性,閱讀者可能並不知道調用的某個變數是全域變數。

2.3.5 常量

常量是指一旦初始化後就不能改變的變數。例如,數位5、字串abc都是常量。C++中使用const關鍵字指定常量,Java使用staticfinal關鍵字指定常量,而Python並沒有提供定義常量的關鍵字。Python是一門功能強大的語言,可以自己定義一個常量類來實現常量的功能。在《Python Cookbook》一書中定義了一個常量模組const


01    class _const:                                 # 定義常量類_const

02        class ConstError(TypeError): pass         # 繼承自TypeError

03        def __setattr__(self,name,value):

04            if self.__dict__.has_key(name):       # 如果__dict__中不包含對應的key則拋出錯誤

05                raise self.ConstError, "Can't rebind const(%s)"%name

06            self.__dict__[name]=value

07    import sys

08    sys.modules[__name__]=_const()                # const註冊進sys.modules的全域dict


【代碼說明】

·這個類定義了一個方法__setattr__()和一個異常類型ConstErrorConstError類繼承自TypeError。通過調用類自帶的字典__dict__,判斷定義的常量是否包含在字典中。如果字典中包含此常量,將拋出異常。否則,給新創建的常量賦值。

·最後兩行代碼的作用是把const類註冊到sys.modules這個全域字典中。

以下代碼在use_const.py中調用const,定義常量。


01      import const

02      const.magic = 23

03      const.magic = 33


【代碼說明】

·第1行代碼導入const模組。

·第2行代碼定義了一個常量magic

·第3行代碼修改常量magic的值,拋出異常。


const.ConstError: Can't rebind const(magic)



2.4 資料類型

資料類型是構成程式設計語言語法的基礎。不同的程式設計語言有不同的資料類型,但都具有常用的幾種資料類型。Python有幾種內置的資料類型——數位、字串、元組、清單和字典。本節將重點介紹數位和字串。

2.4.1 數字

Python3的數位類型分為整型、浮點型、布林型、分數類型、複數類型。使用Python編寫程式時,不需要聲明變數的類型。由Python內置的基底資料型別來管理變數,在程式的後臺實現數值與類型的關聯,以及類型轉換等操作。Python與其他高階語言定義變數的方式及內部原理有很大的不同。在CJava中,定義一個整型的變數,可以採用如下方式表示:


int i = 1;


Python中,定義整型變數的表達方式更簡練。


i = 1


Python根據變數的值自動判斷變數的類型,程式師不需要關心變數究竟是什麼類型,只要知道創建的變數中存放了一個數,以後的工作只是對這個數值進行操作,Python會對這個數的生命週期負責。

更重要的一點是,CJava只是創建了一個int型的普通變數;而Python創建的是一個整型物件,並且Python自動完成了整型物件的創建工作,不再需要通過構造函數創建。Python內部沒有普通類型,任何類型都是對象。如果CJava需要修改變數i的值,只要重新賦值即可;而Python並不能修改對象i的值。例如:


01      #下麵的兩個i並不是同一個對象

02      i = 1

03      print(id(i))

04      i = 2

05      print (id(i))


如果需要查看變數的類型,可以使用Python定義的type類。type__builtin__模組的一個類,該類能返回變數的類型或創建一個新的類型。__builtin__模組是Python的內聯模組,內聯模組不需要import語句,由Python解譯器自動導入。後面還會接觸到更多內聯模組的類和函數。

下面這段代碼返回了各種變數的類型。


01      #整型

02      i = 1

03      print( type(i))

04      #長整型

05      l = 999999999999999999990       # 什麼時候pythonint轉為float跟作業系統位元數相關

06      print type(l)

07      #浮點型

08      f = 1.2

09      print( type(f))

10      #布林型

11      b = True

12      print (type(b))


【代碼說明】

·第3行代碼輸出結果:

·第6行代碼輸出結果:

·第9行代碼輸出結果:

·第12行代碼輸出結果:

Python來進行科學計算也很方便,因為Python內置了複數類型。JavaC#等高階語言則沒有提供複數類型。


01      #複數類型

02      c = 7 + 8j

03      print (type(c))


3行代碼輸出結果:<class'complex'>

注意  複數類型的寫法與數學中的寫法相同,如果寫為c=7+8iPython不能識別其中的“i”,將提示語法錯誤。

2.4.2 字串

Python中有3種表示字串的方式——單引號、雙引號、三引號。單引號和雙引號的作用是一樣的,對於不同的程式師可以根據自己的習慣使用單引號或雙引號。PHP程式師可能更習慣使用單引號表示字串,CJava程式師則習慣使用雙引號表示字串。下面這段代碼中單引號和雙引號的使用是等價的。


01      # 單引號和雙引號的使用是等價的

02      str = "hello world!"

03      print (str)

04      str = 'hello world!'

05      print (str)


【代碼說明】 3行代碼輸出結果:


hello world!


5行代碼輸出結果:


hello world!


三引號的用法是Python特別的語法,三引號中可以輸入單引號、雙引號或換行等字元。


01      # 三引號的用法

02      str = '''he say "hello world!"'''

03      print( str)


【代碼說明】 3行代碼的三引號中帶有雙引號,雙引號也會被輸出。輸出結果:


he say "hello world!"


三引號的另一種用法是製作文檔字串。Python的每個物件都有一個屬性__doc__,這個屬性用於描述該物件的作用。


01      # 三引號製作doc文檔

02      class Hello:

03          '''hello class'''

04          def printHello():

05              '''print hello world'''

06              print ("hello world!")

07      print( Hello.__doc__)

08      print (Hello.printHello.__doc__)


【代碼說明】

·第2行代碼定義了一個名為Hello的類。

·第3行是對Hello類的描述,該字串將被存放在類的__doc__屬性中。

·第4行代碼定義了一個方法printHello()

·第5行代碼描述了printHello(),並把字串存放在該函數的__doc__屬性中。

·第6行代碼輸出結果:


hello world!


·第7行代碼輸出Hello__doc__屬性的內容。輸出結果:


hello class


·第8行代碼輸出printHello()__doc__屬性的內容。輸出結果:


print hello world


如果要輸出含有特殊字元(單引號、雙引號等)的字串,需要使用轉義字元。Python中轉義字元為\,和CJava中的轉義字元相同。轉義操作只要在特殊字元的前面加上\即可。下面這段代碼說明了特殊字元的轉義用法。


01      # 轉義字元

02      str = 'he say:\'hello world!\''

03      print (str)


【代碼說明】 2行代碼中的單引號是特殊字元,需要在'前加上轉義字元。第3行代碼的輸出結果:


he say:'hello world!'


使用雙引號或三引號可以直接輸出含有特殊字元的字串,不需要使用轉義字元。


01      # 直接輸出特殊字元

02      str = "he say:'hello world!'"

03      print (str)

04      str = '''he say:'hello world!' '''

05      print (str)


【代碼說明】

·第2行代碼中使用了雙引號表示字串變數str,因此Python能夠識別出雙引號內部的單引號只是作為輸出的字元。

·第3行代碼的輸出結果:


he say:'hello world!'


·第4行代碼使用三引號表示字串變數str,注意最後一個單引號後面留有一個空格,這個空格是為了讓Python識別出三引號留下的。如果不留下這個空格,4個單引號連在一起,Python解譯器不能正確識別三引號。提示如下錯誤:


SyntaxError: EOL while scanning single-quoted string


·第5行代碼的輸出結果:


he say:'hello world!'


注意  輸出的字串中含有單引號,使用雙引號表示字串即可。相反,輸出的字串中含有雙引號時,可使用單引號表示字串。

 

2.5 運算子與運算式

         Python的運算子號包括算術運算子、關係運算子和邏輯運算子。運算式是由數位或字串和運算子組成的式子。運算式通常用於判斷語句和迴圈語句的條件使用,運算式是學習控制語句編寫的基礎。本節將介紹Python中的各種運算式的使用。
2.5.1 算術運算子和算術運算式
        算術運算子包括四則運算符、求模運算子和求冪運算子。Python中的算術運算子和運算式如表2-2所示。
表2-2 Python中的算術運算子和運算式
        注意 與C、Java語言不同,Python不支援自增運算子和自減運算子。例如,i++、i--是錯誤的語法。
        下面這段代碼演示了Python中算術運算子的使用方法。

01      print( "1 + 1 =", 1 + 1)

02      print ("2 - 1 =", 2 - 1)

03      print( "2 * 3 =", 2 * 3)

04      print ("4 / 2 =", 4 / 2)

05      print( "1 / 2 =", 1 / 2)

06      print ("1 / 2 =", 1.0 / 2.0)

07      print ("3 % 2 =", 3 % 2)

08      print ("2 ** 3 =", 2 ** 3)


【代碼說明】

·第1行代碼的輸出結果:1+1=2

·第2行代碼的輸出結果:2-1=1

·第3行代碼的輸出結果:2*3=6

·第4行代碼的輸出結果:4/2=2

·第5行代碼的輸出結果:1/2=0.5

·第6行代碼中的被除數是1.0,除數是2.0Python把這兩個數作為浮點型處理,因此相除後可以得到正確的結果。輸出結果:1.0/2.0=0.5

·第7行代碼中,求模的值為3除以2後的餘數。輸出結果:3%2=1

·第8行代碼的輸出結果:2**3=8

注意  Python2中執行“1/2”算術運算式的結果略有不同,Python2認為12是整型,相除後的結果會被截斷,因此得到的值為0

Python的算術運算式具有結合性和優先性。結合性是指運算式按照從左往右、先乘除後加減的原則。即從運算式的左邊開始計算,先執行乘法和除法運算,再執行加法和減法運算。例如:


a + b * c % d


以上運算式先執行b*c,然後執行b*c%d,最後執行加法運算。

優先性是指先執行圓括號內的運算式,再按照結合性的原則進行計算。例如:


(a + b) * (c % d)


以上運算式先計算a+b的值,然後計算c%d的值,最後把兩個值相乘。下面這段代碼演示了算數運算的優先順序。


01      # 算數運算的優先順序

02      a = 1

03      b = 2

04      c = 3

05      d = 4

06      print ("a + b * c % d =", a + b * c % d)

07      print ("(a + b) * (c % d) =", (a + b) * (c % d))


6行代碼的輸出結果:


a + b * c % d = 3


7行代碼的輸出結果:


(a + b) * (c % d) = 9


2.5.2 關係運算子和關聯運算式

關係運算子即對兩個物件進行比較的符號。Python中的關係運算子和運算式如表2-3所示。

表2-3 Python中的關係運算子和運算式
        下面這段代碼演示了關聯運算式的邏輯輸出。

01      # 關聯運算式

02      print (2 > 1)

03      print (1 <= 2)

04      print (1 == 2)

05      print (1 != 2)


【代碼說明】

·第2行代碼,2>1的邏輯正確。輸出結果:True

·第3行代碼,1<=2的邏輯正確。輸出結果:True

·第4行代碼,1==2的邏輯錯誤。輸出結果:False

·第5行代碼,1!=2的邏輯正確。輸出結果:True

不同的關係運算子優先順序別不同。其中<<=>>=4個運算子的優先順序別相等,==!=的優先順序別相等。而<<=>>=的優先順序別大於==!=的優先順序別。例如:a>=b==c等價於(a>=b)==c

關係運算子的優先順序低於算術運算子。下面的代碼演示了關係運算子的優先順序別。


01      # 關聯運算式的優先順序別

02      print( "1 + 2 < 3 - 1 =>", 1 + 2, "<", 3 - 1, "=>", 1 + 2 < 3 - 1)

03      print ("1 + 2 <= 3 > 5 % 2 =>", 1 + 2, "<=", 3, ">", 5 % 2, "=>", 1 + 2 <= 3 > 5 % 2)


【代碼說明】

·第2行代碼,先執行1+2=3,然後執行31=2,最後比較3<2。輸出結果:


1 + 2 < 3 - 1 => 3 < 2 => False


·第3行代碼,先執行1+2=3,然後執行5%2=1,最後比較3<=3>1。輸出結果:


1 + 2 <= 3 > 5 % 2 => 3 <= 3 > 1 => True


2.5.3 邏輯運算子和邏輯運算式

邏輯運算式是用邏輯運算子和變數連接起來的式子。任何語言的邏輯運算子都只有3——邏輯與、邏輯或和邏輯非。CJava語言的邏輯運算子用&&||!表示,Python採用andornot表示。表2-4列出了Python中的邏輯運算子和運算式。

表2-4 Python中的邏輯運算子和運算式

        下面的代碼演示了邏輯運算式的運算。

01      # 邏輯運算子

02      print( not True)

03      print( False and True)

04      print (True and False)

05      print (True or False)


【代碼說明】

·第2行代碼,True的邏輯非為False。輸出結果:False

·第3行代碼,檢測到and運算子左側的False,就直接返回False。輸出結果:False

·第4行代碼,檢測到and運算子左側為True,然後繼續檢測右側,右側的值為False,於是返回False。輸出結果:False

·第5行代碼,or運算子的左側為True,於是返回True。輸出結果:True

邏輯非的優先順序大於邏輯與和邏輯或的優先順序,而邏輯與和邏輯或的優先順序相等。邏輯運算子的優先順序低於關係運算子,必須先計算關係運算子,然後再計算邏輯運算子。下面這段代碼演示了邏輯運算子、關係運算子和算術運算子的優先順序別。


01      # 邏輯運算式的優先順序別

02      print( "not 1 and 0 =>", not 1 and 0)

03      print( "not (1 and 0) =>", not (1 and 0))

04      print ("(1 <= 2) and False or True =>", (1 <= 2) and False or True)

05      print ("(1 <= 2) or 1 > 1 + 2 =>", 1 <= 2, "or", 1 > 2, "=>", (1 <= 2) or (1 < 2))


【代碼說明】

·第2行代碼,先執行not 1,再執行and運算。輸出結果:


not 1 and 0 => False


·第3行代碼,先執行括弧內的1 and 0,再執行not運算。輸出結果:


not (1 and 0) => True


·第4行代碼,先執行1<=2的關係運算運算式,再執行and運算,最後執行or運算。輸出結果:


(1 <= 2) and False or True => True


·第5行代碼,先執行1<=2的關係運算運算式,再執行運算式1>1+2,最後執行or運算。輸出結果:


(1 <= 2) or 1 > 1 + 2 => True or False => True



2.6 小結

        本章講解了Python的基本語法和基本概念,包括Python的檔案類型、編碼規則、變數、資料類型以及運算子和運算式等內容。重點講解了Python的編碼規則、命名規則、縮進式的寫法、注釋等。Python中的變數與C、Java有很大的不同,本章通過比較的方式闡述Python變數的分類和特性。最後講解了運算子和運算式的知識,這些知識是編寫控制語句的基礎。下一章將會學習到Python控制語句的編寫,控制語句將用到本章介紹的變數和運算式。條件陳述式和迴圈語句都是通過運算式返回的布林值控制程式流程的,這些內容是編寫程式的基本要素。

2.7 習題

1.以下變數命名不正確的是( )。
A.foo=the_value
B.foo=1_value
C.foo=_value
D.foo=value_&
2.計算2的38次方的值。
3.以下邏輯運算的結果:
a)True and False
b)False and True
c)True or False
d)False or True
e)True or False and True
f)True and False or False
4.編寫程式計算1+2+3+…+100的結果。

0 留言:

發佈留言