Python語法及語義

Python語法及語義(Python syntax and semantics),Python編程語言語法是一組Python的運作編程規則,用於定義Python程序如何來編寫與編譯(由運行時系統程式師來操作)。[1]Python語言與PerlC,以及Java有許多相似之處,不過在這些語言之間仍存在著一些明確的差異[1]

設計理念

Python被設計為一種高度可讀的程式語言。[2]它具有相對排列有致的視覺佈局,並且在其他編程語言使用標點符號的情況下經常使用英文關鍵字。Python的目標是在其語法設計中保持簡單與一致性,在「Python之禪」一書中的「宣示」也提到「應該有一種——最好只有一種——明顯的方法來達成Python簡單與一致性的目標」。[3]

如上述、Python的「宣示」即「應該有一種——最好只有一種——明顯的方法來達成Python簡單與一致性的目標」;而這個宣示故意跟「Perl」及「Ruby」的宣示「不止只有一種方法可以達成目標」唱反調。

關鍵字

Python有35個關鍵字或保留字;這些關鍵字或保留字不能用作標識符[4][5]

  • and
  • as
  • assert
  • async[note 1]
  • await[note 1]
  • break
  • class
  • continue
  • def
  • del
  • elif
  • else
  • except
  • False[note 2]
  • finally
  • for
  • from
  • global
  • if
  • import
  • in
  • is
  • lambda
  • None
  • nonlocal[note 3]
  • not
  • or
  • pass
  • raise
  • return
  • True[note 2]
  • try
  • while
  • with
  • yield
註解
  1. ^ 1.0 1.1 asyncawait自Python 3.5启用[6]
  2. ^ 2.0 2.1 TrueFalse在Python 3.0中成为保留字,此前为全局变量。
  3. ^ nonlocal自Python 3.0启用。

首行縮排

Python 使用空白字元來分隔控制流程區塊(遵循越位規則)。 Python 借鑒了其前身ABC 的這一特性:它使用首行縮排來表示代碼塊的運作,而不是標點符號關鍵字

在所謂的"自由格式"(free-format)的程式語言中——使用源自ALGOL的區塊結構——代碼區塊會使用大括號({ })或關鍵字來區分隔開。在這些程式語言的大多數代碼約定中,程序師通常會在區塊內縮進代碼,使其在視覺上能與周圍的代碼區明白的區分開來。

一個名為foo遞迴函數,它傳遞一個參數x,如果參數為0,僅呼叫bar這個函數;否則將呼叫不同的名為baz的函數,傳遞參數x,並遞迴呼叫自身,傳遞 x-1為參數 , 這都可以在 Python 中實現的演算:

def foo(x):
    if x == 0:
        bar()
    else:
        baz(x)
        foo(x - 1)

並且可以在C中用K&R缩排樣式寫成如下程式:

void foo(int x)
{
    if (x == 0) {
        bar();
    } else {
        baz(x);
        foo(x - 1);
    }
}

不正確的縮排代碼可能會被人誤讀,而不是被編譯器直譯器解譯。 比如,如果上例中最後一行的函數呼叫 foo(x - 1)會被錯誤地縮排到if/else區塊之外:

def foo(x):
    if x == 0:
        bar()
    else:
        baz(x)
    foo(x - 1)

造成上例編程無論如何最後一行都會被執行,即使x0亦是如此,結果會導致無限遞迴的現象。

雖然空格製表鍵都被接受作為縮排的形式,並且可以使用任意倍數的空格,也建議使用空格作為不同的架構區分[7];並且在標準上建議區分不同的巢狀結構使用 4 個空格的形式(如上例所示),這也是迄今為止最常用的空格佈局形態。[8][9]從 Python 3[10]開始,不允許在同一源代碼文件中的連續行上混合空格和製表符,這會導致產生難以發現的錯誤;因為有許多工具無法在視覺上區分空格與製表鍵。

資料結構

由於 Python 是一種動態類型的程式語言,因此 Python 值不是一種變量攜帶資料類型信息。 Python 中的所有變量都持有對對象參照,這些參照被傳遞給函數; 函數不能在其呼叫函數中更改變量參照的值(但請參閱下面的例外情況)。有些人(包括吉多·範羅蘇姆本人)將這種參數傳遞方案稱為"依物件參照呼叫"。"對象參照"意味著一個名稱,"傳遞參照"是一個"別名",即是對同一物件的參照副本,就像在 C/C++ 中一樣。物件的值可以在被呼叫的函數中用“別名”改變,比如:

>>> alist = ['a', 'b', 'c']
>>> def my_func(al):
...     al.append('x')
...     print(al)
...
>>> my_func(alist)
['a', 'b', 'c', 'x']
>>> alist
['a', 'b', 'c', 'x']

函數my_func用形式引數al改變alist的值,它即是alist的別名。然而、任何對別名本身進行操作的嘗試都不會對原始對象產生影響。在Python中,非最內部局部(non-innermost-local)以及未聲明全面(not-declared-global)可攫取的名稱都是別名。

在動態類型語言中,Python 進行了適度的類型檢查。 隱式轉換是為數字類型(以及布爾值)定義的,因此可以有效地將複數乘以整數(例如)而沒有顯式轉換。 但是,例如數字和字符串之間沒有隱式轉換; 字符串是需要數字之數學函數的無效參數。

基本類型

Python 具有廣泛的基本數據類型。 除了傳統的整數以及浮點數算術外,它透明地支持高精度計算複數,及十進制數英语Decimal data type

集合類型

Python非常有用的方面之的是集合(或容器)類型的概念。 一般來說,集合是一個以易於引用、或則索引的方式包含其他對象的物件。 集合有兩種基本形式:序列與映射。

物件系統

在Python中,一切都是物件,甚至是類屬。類屬,作為物件,有一個類屬,稱為它們的元類。Python亦支持多重繼承以及Mixin

Python語言支持對類型以及類屬的廣泛自檢。 可以讀取與比較類型——而所謂的類型是指類型的實例。 物件的屬性可以提取為字典。

文字

字符串

Python有各種字符串文字英语String literal

普通字符串文字

單引號雙引號均可用於引用字符串。 與Unix shell語言、Perl,或受Perl影響的語言(諸如RubyGroovy)不同,單引號和雙引號的功能相同,即$foo表達式沒有字符串插值。然而,可以通過多種方式進行插值:使用“f-strings”(自Python 3.6[11]開始)、使用格式方法或者舊的%字符串格式的運算符。

比如,所有這些 Python 的宣告語句:

print(f"I just printed {num} pages to the printer {printer}")

print("I just printed {} pages to the printer {}".format(num, printer))
print("I just printed {0} pages to the printer {1}".format(num, printer))
print("I just printed {num} pages to the printer {printer}".format(num=num, printer=printer))

print("I just printed %s pages to the printer %s" % (num, printer))
print("I just printed %(num)s pages to the printer %(printer)s" % {"num": num, "printer": printer})

等價於Perl的宣告語句:

print "I just printed $num pages to the printer $printer\n"

語句使用變量num以及printer構建一個字符串。

多行字符串文字

還有多行字符串,它們以一系列三個單引號或雙引號開頭及結尾,而其功能類似於PerlRuby中的Here文档

字符串內插值英语String interpolation的一個簡單示例(使用格式的方法)比如:

print("""Dear {recipient},

I wish you to leave Sunnydale and never return.

Not Quite Love,
{sender}
""".format(sender="Buffy the Vampire Slayer", recipient="Spike"))

原始字符串

範例包括:

>>> # A Windows path, even raw strings cannot end in a backslash
>>> r"C:\Foo\Bar\Baz\"
  File "<stdin>", line 1
    r"C:\Foo\Bar\Baz\"
                     ^
SyntaxError: EOL while scanning string literal

>>> dos_path = r"C:\Foo\Bar\Baz\ " # avoids the error by adding
>>> dos_path.rstrip()              # and removing trailing space
'C:\\Foo\\Bar\\Baz\\'

>>> quoted_dos_path = r'"{}"'.format(dos_path)
>>> quoted_dos_path
'"C:\\Foo\\Bar\\Baz\\ "'

>>> # A regular expression matching a quoted string with possible backslash quoting
>>> re.match(r'"(([^"\\]|\\.)*)"', quoted_dos_path).group(1).rstrip()
'C:\\Foo\\Bar\\Baz\\'

>>> code = 'foo(2, bar)'
>>> # Reverse the arguments in a two-arg function call
>>> re.sub(r'\(([^,]*?),([^ ,]*?)\)', r'(\2, \1)', code)
'foo(2, bar)'
>>> # Note that this won't work if either argument has parens or commas in it.

相鄰字符串文字的串聯

因此

title = "One Good Turn: " \
        'A Natural History of the Screwdriver and the Screw'

相當於

title = "One Good Turn: A Natural History of the Screwdriver and the Screw"

Unicode

從Python 3.0開始,源代碼與直譯器的默認字符集都是UTF-8。在UTF-8中,Unicode字符串的處理方式及傳統的字節字符串類似。這個例子將起如下作用:

s = "Γειά" # Hello in Greek
print(s)

數字

Python中的數字文字是屬於普通類型,例如:0-13.43.5e-8

列表、多元组、集合、字典

Python具有支持創建容器類型的語法。

列表(類屬列表)是任意類型項目的可變序列,可以使用特殊語法來創建。

a_list = [1, 2, 3, "a dog"]

或使用普通物件來創建

a_second_list = list()
a_second_list.append(4)
a_second_list.append(5)

多元組(類屬元組)是任意類型項目的不可變序列。還有一個特殊的語法來創建多元組

a_tuple = 1, 2, 3, "four"
a_tuple = (1, 2, 3, "four")

運算符

算術

在算術上Python包括+-*/("真值除法")、//(下限除法)、%(模數)以及**(取冪)運算符,以及它們通常的數學優先級別。在Python 3中,x/y執行"真值除法"(true division),這表示它總是會回返一個浮點數,即使x與y都是整除的整數

>>> 4 / 2
2.0

接著//執行整數除法、或下限除法,將商數的下限作為整數回返。

在Python 2(及大多數其他編程語言)中,除非明確要求,否則x/y在執行整數除法時,僅當任一輸入值為浮點數時才回返浮點數。但是,因爲Python是一種動態類型的語言;並不總是能夠分辨出正在執行的是哪一個操作,這往往會導致細微的錯誤。從而促使Python 3引入//運算符,並改變/運算符的語義。

比較運算符

比較運算符(comparison operator),比如:==, !=, <, >, <=, >=, is, is not, innot in[12]可用於各種運算值的使用上。數字、字符串、序列,以及映射都可以進行比較。在Python 3中,不同的類型(比如strint)沒有一致的相對順序。雖然在Python 2中可以比較某個字符串是大於、還是小於某個整數;但這被認為是歷史上的"設計怪癖",最終在Python 3中被刪除。

諸如a < b < c之類的"鍊式比較表達式"(chained comparison expressions)大致具有它們在數學中的含義,而不是在C與類似語言中發現的不尋常含義。這些程式術語是依據順序進行評估與比較。這個操作具有短路求值語義,這表示著一旦判斷明確,就保證評估停止:如果a < b為假,則永遠不會評估到c,因為表達式不可能再為真。

對於沒有副作用的表達式,a < b < c為等價於a < b 與 b < c。然而,當表達式具有副作用時,也是存在很大差異。a < f(x) < b將只計算f(x)一次,而a < f(x) 以及 f(x) < b;如果a的值小於f(x)將計算兩次,否則只計算一次。

邏輯運算符

在所有版本的Python裏面,布爾運算符處理零值或空值、諸如""0None0.0[],以及{}將被視為”假“;而通常則將非空(non-empty)、非零值(non-zero value)視為"真"。布爾值在Python 2.2.1中是作為常數(從10產生的子類化)添加到程式語言裏,並在Python 3中變成為完整的關鍵字。二進制"比較運算符"、諸如==以及>則回返的信息。

布爾運算符(boolean operator)andor使用最小化求值。例如,y == 0 或則 x/y > 100永遠不會引發被零除之異常錯誤信息。這些"運算符"回返最後操作元評估之值,而不是或是的情形。因此,表達式(4 及 5)的計算結果為 5,而(4 或 5)的計算結果為 4。因此,表達式(4 及 5)的計算結果為 5,而(4 或 5)的計算結果為4

函數式編程

如上所述,Python的另一個優勢是函數式編程風格的可用性。正如所料,這使得使用列表及其他集合更加簡單。

理解

L = [mapping_expression for element in source_list if filter_expression]

第一級函數

在Python中,函數是可以動態創建以及傳遞的第一級物件。

Python對匿名函數的有限支持是lambda建構式。下列範例是對輸入值求平方的匿名函數,使用引數5來呼叫(調用)的:

f = lambda x: x**2
f(5)

Lambda僅限於包含表達式英语Expression (computer science)而不是語句陳述式,儘管"控制流"(control flow)仍可以通過使用短路在lambda中不太優雅地實現編程,[13]並且更習慣地使用"條件表達式"(conditional expression)。[14]

閉包

def derivative(f, dx):
    """Return a function that approximates the derivative of f
    using an interval of dx, which should be appropriately small.
    """
    def function(x):
        return (f(x + dx) - f(x)) / dx
    return function

生成器

from itertools import count

def generate_primes(stop_at=None):
    primes = []
    for n in count(start=2):
        if stop_at is not None and n > stop_at:
            return # raises the StopIteration exception
        composite = False
        for p in primes:
            if not n % p:
                composite = True
                break
            elif p ** 2 > n:
                break
        if not composite:
            primes.append(n)
            yield n

生成器表達式

在Python 2.4中引入的生成器表達式是列表推導式的惰性求值等價式。使用上一節中提供的質數生成器,我們可以定義一個惰性求值方式但不是無限的集合。

from itertools import islice

primes_under_million = (i for i in generate_primes() if i < 1000000)
two_thousandth_prime = islice(primes_under_million, 1999, 2000).next()
primes_under_million = [i for i in generate_primes(2000000) if i < 1000000]
two_thousandth_prime = primes_under_million[1999]

字典與集合推導式

>>> dict((n, n*n) for n in range(5))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Python 2.7及3.0通過引入字典與集合推導式來統一所有集合類型,類似於列表推導式:

>>> [n*n for n in range(5)]  # regular list comprehension
[0, 1, 4, 9, 16]
>>>
>>> {n*n for n in range(5)}  # set comprehension
{0, 1, 4, 9, 16}
>>>
>>> {n: n*n for n in range(5)}  # dict comprehension
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

物件

Python支持大多數面向物件編程(OOP)技術。

With 宣告

關於with語句可以處理資源的使用,並允許使用者使用"上下文管理器協議"(Context Manager protocol)。[15]在進入作用域時呼叫一個函數(__enter__()),而自離開時呼叫另一個函數(__exit__())。如此可以防止忘記釋放資源,而且還可以處理更複雜的情形、比如在資源使用過程中發生異常時可以釋放資源。"上下文管理器"(Context Managers)通常與文件、資料庫連接、測試用例等情況一起使用。

屬性

屬性允許使用及屬性揭取相同的語法在物件實例上調用專門定義的方法。 定義一些屬性的類屬的一個例子是:

class MyClass:
    def __init__(self):
        self._a = None

    @property
    def a(self):
        return self._a

    @a.setter  # makes the property writable
    def a(self, value):
        self._a = value

描述符

定義三個特殊方法__get__(self, instance, owner)__set__(self, instance, value)__delete__(self, instance)) 中的一個,或多個的類屬可以用作描述符。創建描述符的實例作為第二個類的類成員,使該實例成為第二個類之屬性。[16]

類屬與靜態法

Python允許通過使用@cla @classmethod@staticmethod修飾模式來創建類方法和靜態方法。方法的第一個參數是類物件,而不是對實例的自引用。靜態方法沒有特殊的第一個參數。實例及類物件都不會傳遞給靜態方法

異常

在第一個代碼的示例中,遵循LBYL的方法,在揭取之前對屬性進行了外顯的檢查:

if hasattr(spam, 'eggs'):
    ham = spam.eggs
else:
    handle_missing_attr()

第二個示例遵循EAFP範式:

try:
    ham = spam.eggs
except AttributeError:
    handle_missing_attr()

説明與文檔字符串

説明一段代碼:

import sys

def getline():
    return sys.stdin.readline()  # Get one line and return it

用多行説明一段代碼:

def getline():
    return sys.stdin.readline()    """this function
                                      gets one line
                                      and returns it"""

函數註解

函數註解(類型提示)在PEP3107中定義。[17]函數註解允許將數據附加到函數的參數以及返回原址。註釋的特性不是由程式語言所定義的,而是留給第三方框架的來進行的。例如,可以編寫一個數據庫來處理靜態類型的程式語言架構:[17]

def haul(item: Haulable, *vargs: PackAnimal) -> Distance

修飾子

修飾子(裝飾器;修飾符)是用於修改函數、方法,或則類定義的任何可調用的 Python 物件。修飾子傳遞正被定義的原始物件、並返回修改後的物件;然後將其綁定到定義中的名稱之上。Python 修飾子部分受到 Java 註釋運用的啟發,並且具有類似 Java語法;修飾子語法是純粹的語法糖,使用 @ 作為修飾子的關鍵字

@viking_chorus
def menu_item():
    print("spam")

相當於

def menu_item():
    print("spam")
menu_item = viking_chorus(menu_item)

修飾子是元編程的一種形式; 它們增強了它們修飾的函數或方法的作用。 比如,在下面的示例中,viking_chorus 可能會導致 menu_item 每次被呼叫時運行 8 次(參見小品喜劇英语Spam (Monty Python)(Spam)):

def viking_chorus(myfunc):
    def inner_func(*args, **kwargs):
        for i in range(8):
            myfunc(*args, **kwargs)
    return inner_func

函數修飾子的典型用途是創建類方法靜態方法、添加函數屬性、追踪英语Tracing (software)、設置前置後置條件以及同步[18]不過可以用於更多方面,包括尾遞歸[19]記憶化;甚至改進其他修飾子的編寫。[20]

修飾子的作用;即是可以經由在主要程式語言相鄰且相關的行之上置放幾個修飾子、進而來帶動主程式鏈接修飾子之間的連繫:

@invincible
@favourite_colour("Blue")
def black_knight():
    pass

相當於

def black_knight():
    pass
black_knight = invincible(favourite_colour("Blue")(black_knight))

或者,使用中介架構的變數、如下程式所示:

def black_knight():
    pass
blue_decorator = favourite_colour("Blue")
decorated_by_blue = blue_decorator(black_knight)
black_knight = invincible(decorated_by_blue)

在上述的例子中,favourite_colour 修飾子工廠接受一個參數。 修飾子工廠必須返回一個修飾子,然後使用要修飾的物件作為參數來呼叫它:

def favourite_colour(colour):
    def decorator(func):
        def wrapper():
            print(colour)
            func()
        return wrapper
    return decorator

復活節彩蛋

一個隱藏信息,Python之禪Python設計哲學的總結),在嘗試import this(導入this) 時會顯示出來。

註釋

  1. ^ 1.0 1.1 "Readability counts." - PEP 20 - The Zen of Python页面存档备份,存于互联网档案馆
  2. ^ "Readability counts." - PEP 20 - The Zen of Python 互联网档案馆存檔,存档日期2014-12-05.
  3. ^ PEP 20 - The Zen of Python. Python Software Foundation. 2004-08-23 [2008-11-24]. (原始内容存档于2008-12-03). 
  4. ^ 2. Lexical analysis. Python 3 documentation. Python Software Foundation. [2021-03-11]. (原始内容存档于2018-01-09). 
  5. ^ 2. Lexical analysis. Python v2.7.18 documentation. Python Software Foundation. [2021-03-11]. (原始内容存档于2022-05-02). 
  6. ^ New Keywords. Python v3.5 documentation. Docs.python.org. [2016-06-01]. (原始内容存档于2016-06-18). 
  7. ^ PEP 8 -- Style Guide for Python Code. Python.org. [2021-03-17]. (原始内容存档于2018-07-13) (英语). 
  8. ^ Hoffa, Felipe. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?. Medium. 2017-07-26 [2021-03-11]. (原始内容存档于2022-03-18) (英语). 
  9. ^ Tabs or Spaces. ukupat.github.io. [2021-03-11]. (原始内容存档于2022-05-05). 
  10. ^ PEP 8 -- Style Guide for Python Code. Python.org. [2021-03-11]. (原始内容存档于2018-07-13) (英语). 
  11. ^ PEP 498 - Literal String Interpolation. What’s New In Python 3.6. 2016-12-23 [2017-03-29]. (原始内容存档于2017-03-30). 
  12. ^ 6. Expressions — Python 3.9.2 documentation. docs.python.org. [2021-03-17]. (原始内容存档于2022-05-10). 
  13. ^ David Mertz. Functional Programming in Python. IBM developerWorks. [2007-08-27]. (原始内容存档于2007-02-20). 
  14. ^ PEP 308 -- Conditional Expressions. [2016-04-14]. (原始内容存档于2016-03-13). 
  15. ^ PEP 343 -- The "with" Statement. [2014-08-15]. (原始内容存档于2014-12-14). 
  16. ^ Glossary — Python 3.9.2 documentation. docs.python.org. [2021-03-23]. (原始内容存档于2020-06-25). 
  17. ^ 17.0 17.1 PEP 3107 -- Function Annotations. [2014-08-15]. (原始内容存档于2015-01-06). 
  18. ^ Python 2.4 Decorators: Reducing code duplication and consolidating knowledge. Dr. Dobb's. 2005-05-01 [2007-02-08]. (原始内容存档于2007-02-06). 
  19. ^ New Tail Recursion Decorator. ASPN: Python Cookbook. 2006-11-14 [2007-02-08]. (原始内容存档于2007-02-09). 
  20. ^ The decorator module. [2007-02-08]. (原始内容存档于2007-02-10). 

參閱

外部連接