国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

SICP Python 描述 1.3 定義新的函數

SegmentFault / 2066人閱讀

摘要:到目前為止,我們的環境只包含全局幀。要注意函數名稱是重復的,一個在幀中,另一個是函數的一部分。運算符字表達式是全局幀中發現的名稱,綁定到了內建的加法函數上。嚴格來說,這并不是問題所在不同局部幀中的的綁定是不相關的。

1.3 定義新的函數

來源:1.3 Defining New Functions

譯者:飛龍

協議:CC BY-NC-SA 4.0

我們已經在 Python 中認識了一些在任何強大的編程語言中都會出現的元素:

數值是內建數據,算數運算是函數。

嵌套函數提供了組合操作的手段。

名稱到值的綁定提供了有限的抽象手段。

現在我們將要了解函數定義,一個更加強大的抽象技巧,名稱通過它可以綁定到復合操作上,并可以作為一個單元來引用。

我們通過如何表達“平方”這個概念來開始。我們可能會說,“對一個數求平方就是將這個數乘上它自己”。在 Python 中就是:

>>> def square(x):
        return mul(x, x)

這定義了一個新的函數,并賦予了名稱square。這個用戶定義的函數并不內建于解釋器。它表示將一個數乘上自己的復合操作。定義中的x叫做形式參數,它為被乘的東西提供一個名稱。這個定義創建了用戶定義的函數,并且將它關聯到名稱square上。

函數定義包含def語句,它標明了(名稱)和一列帶有名字的(形式參數)。之后,return(返回)語句叫做函數體,指定了函數的(返回表達式),它是函數無論什么時候調用都需要求值的表達式。

def ():
    return 

第二行必須縮進!按照慣例我們應該縮進四個空格,而不是一個Tab,返回表達式并不是立即求值,它儲存為新定義函數的一部分,并且只在函數最終調用時會被求出。(很快我們就會看到縮進區域可以跨越多行。)

定義了square之后,我們使用調用表達式來調用它:

>>> square(21)
441
>>> square(add(2, 5))
49
>>> square(square(3))
81

我們也可以在構建其它函數時,將square用作構建塊。列入,我們可以輕易定義sum_squares函數,它接受兩個數值作為參數,并返回它們的平方和:

>>> def sum_squares(x, y):
        return add(square(x), square(y))
>>> sum_squares(3, 4)
25

用戶定義的函數和內建函數以同種方法使用。確實,我們不可能在sum_squares的定義中分辨出square是否構建于解釋器中,從模塊導入還是由用戶定義。

1.3.1 環境

我們的 Python 子集已經足夠復雜了,但程序的含義還不是非常明顯。如果形式參數和內建函數具有相同名稱會如何呢?兩個函數是否能共享名稱而不會產生混亂呢?為了解決這些疑問,我們必須詳細描述環境。

表達式求值所在的環境由幀的序列組成,它們可以表述為一些盒子。每一幀都包含了一些綁定,它們將名稱和對應的值關聯起來。全局幀只有一個,它包含所有內建函數的名稱綁定(只展示了absmax)。我們使用地球符號來表示全局。

賦值和導入語句會向當前環境的第一個幀添加條目。到目前為止,我們的環境只包含全局幀。

>>> from math import pi
>>> tau = 2 * pi

def語句也將綁定綁定到由定義創建的函數上。定義square之后的環境如圖所示:

這些環境圖示展示了當前環境中的綁定,以及它們所綁定的值(并不是任何幀的一部分)。要注意函數名稱是重復的,一個在幀中,另一個是函數的一部分。這一重復是有意的,許多不同的名字可能會引用相同函數,但是函數本身只有一個內在名稱。但是,在環境中由名稱檢索值只檢查名稱綁定。函數的內在名稱不在名稱檢索中起作用。在我們之前看到的例子中:

>>> f = max
>>> f

名稱max是函數的內在名稱,以及打印f時我們看到的名稱。此外,名稱maxf在全局環境中都綁定到了相同函數上。

在我們介紹 Python 的附加特性時,我們需要擴展這些圖示。每次我們這樣做的時候,我們都會列出圖示可以表達的新特性。

新的環境特性:賦值和用戶定義的函數定義。

1.3.2 調用用戶定義的函數

為了求出運算符為用戶定義函數的調用表達式,Python 解釋器遵循與求出運算符為內建函數的表達式相似的過程。也就是說,解釋器求出操作數表達式,并且對產生的實參調用具名函數。

調用用戶定義的函數的行為引入了第二個局部幀,它只能由函數來訪問。為了對一些實參調用用戶定義的函數:

在新的局部幀中,將實參綁定到函數的形式參數上。

在當前幀的開頭以及全局幀的末尾求出函數體。

函數體求值所在的環境由兩個幀組成:第一個是局部幀,包含參數綁定,之后是全局幀,包含其它所有東西。每個函數示例都有自己的獨立局部幀。

這張圖包含兩個不同的 Python 解釋器層面:當前的環境,以及表達式樹的一部分,它和要求值的代碼的當前一行相關。我們描述了調用表達式的求值,用戶定義的函數(藍色)表示為兩部分的圓角矩形。點線箭頭表示哪個環境用于在每個部分求解表達式。

上半部分展示了調用表達式的求值。這個調用表達式并不在任何函數里面,所以他在全局環境中求值。所以,任何里面的名稱(例如square)都會在全局幀中檢索。

下半部分展示了square函數的函數體。它的返回表達式在上面的步驟1引入的新環境中求值,它將square的形式參數x的名稱綁定到實參的值-2上。

環境中幀的順序會影響由表達式中的名稱檢索返回的值。我們之前說名稱求解為當前環境中與這個名稱關聯的值。我們現在可以更精確一些:

名稱求解為當前環境中,最先發現該名稱的幀中,綁定到這個名稱的值。

我們關于環境、名稱和函數的概念框架建立了求值模型,雖然一些機制的細節仍舊沒有指明(例如綁定如何實現),我們的模型在描述解釋器如何求解調用表示上,變得更準確和正確。在第三章我們會看到這一模型如何用作一個藍圖來實現編程語言的可工作的解釋器。

新的環境特性:函數調用。

1.3.3 示例:調用用戶定義的函數

讓我們再一次考慮兩個簡單的定義:

>>> from operator import add, mul
>>> def square(x):
        return mul(x, x)
>>> def sum_squares(x, y):
        return add(square(x), square(y))

以及求解下列調用表達式的過程:

>>> sum_squares(5, 12)
169

Python 首先會求出名稱sum_squares,它在全局幀綁定了用戶定義的函數。基本的數字表達式 5 和 12 求值為它們所表達的數值。

之后,Python 調用了sum_squares,它引入了局部幀,將x綁定為 5,將y綁定為 12。

這張圖中,局部幀指向它的后繼,全局幀。所有局部幀必須指向某個先導,這些鏈接定義了當前環境中的幀序列。

sum_square的函數體包含下列調用表達式:

   add     (  square(x)  ,  square(y)  )
 ________     _________     _________
"operator"   "operand 0"   "operand 1"

全部三個子表達式在當前環境中求值,它開始于標記為sum_squares的幀。運算符字表達式add是全局幀中發現的名稱,綁定到了內建的加法函數上。兩個操作數子表達式必須在加法函數調用之前依次求值。兩個操作數都在當前環境中求值,開始于標記為sum_squares的幀。在下面的環境圖示中,我們把這一幀叫做A,并且將指向這一幀的箭頭同時替換為標簽A

在使用這個局部幀的情況下,函數體表達式mul(x, x)求值為 25。

我們的求值過程現在輪到了操作數 1,y的值為 12。Python 再次求出square的函數體。這次引入了另一個局部環境幀,將x綁定為 12。所以,操作數 1 求值為 144。

最后,對實參 25 和 144 調用加法會產生sum_squares函數體的最終值:169。

這張圖雖然復雜,但是用于展示我們目前為止發展出的許多基礎概念。名稱綁定到值上面,它延伸到許多局部幀中,局部幀在唯一的全局幀之上,全局幀包含共享名稱。表達式為樹形結構,以及每次子表達式包含用戶定義函數的調用時,環境必須被擴展。

所有這些機制的存在確保了名稱在表達式中正確的地方解析為正確的值。這個例子展示了為什么我們的模型需要所引入的復雜性。所有三個局部幀都包含名稱x的綁定。但是這個名稱在不同的幀中綁定到了不同的值上。局部幀分離了這些名稱。

1.3.4 局部名稱

函數實現的細節之一是實現者對形式參數名稱的選擇不應影響函數行為。所以,下面的函數應具有相同的行為:

>>> def square(x):
        return mul(x, x)
>>> def square(y):
        return mul(y, y)

這個原則 -- 也就是函數應不依賴于編寫者選擇的參數名稱 -- 對編程語言來說具有重要的結果。最簡單的結果就是函數參數名稱應保留在函數體的局部范圍中。

如果參數不位于相應函數的局部范圍中,square的參數x可能和sum_squares中的參數x產生混亂。嚴格來說,這并不是問題所在:不同局部幀中的x的綁定是不相關的。我們的計算模型具有嚴謹的設計來確保這種獨立性。

我們說局部名稱的作用域被限制在定義它的用戶定義函數的函數體中。當一個名稱不能再被訪問時,它就離開了作用域。作用域的行為并不是我們模型的新事實,它是環境的工作方式的結果。

1.3.5 實踐指南:選擇名稱

可修改的名稱并不代表形式參數的名稱完全不重要。反之,選擇良好的函數和參數名稱對于函數定義的人類可解釋性是必要的。

下面的準則派生于 Python 的代碼風格指南,可被所有(非反叛)Python 程序員作為指南。一些共享的約定會使社區成員之間的溝通變得容易。遵循這些約定有一些副作用,我會發現你的代碼在內部變得一致。

函數名稱應該小寫,以下劃線分隔。提倡描述性的名稱。

函數名稱通常反映解釋器向參數應用的操作(例如printaddsquare),或者結果(例如maxabssum)。

參數名稱應小寫,以下劃線分隔。提倡單個詞的名稱。

參數名稱應該反映參數在函數中的作用,并不僅僅是滿足的值的類型。

當作用非常明確時,單個字母的參數名稱可以接受,但是永遠不要使用l(小寫的L)和O(大寫的o),或者I(大寫的i)來避免和數字混淆。

周期性對你編寫的程序復查這些準則,不用多久你的名稱會變得十分 Python 化。

1.3.6 作為抽象的函數

雖然sum_squares十分簡單,但是它演示了用戶定義函數的最強大的特性。sum_squares函數使用square函數定義,但是僅僅依賴于square定義在輸入參數和輸出值之間的關系。

我們可以編寫sum_squares,而不用考慮如何計算一個數值的平方。平方計算的細節被隱藏了,并可以在之后考慮。確實,在sum_squares看來,square并不是一個特定的函數體,而是某個函數的抽象,也就是所謂的函數式抽象。在這個層級的抽象中,任何能計算平方的函數都是等價的。

所以,僅僅考慮返回值的情況下,下面兩個計算平方的函數是難以區分的。每個都接受數值參數并且產生那個數的平方作為返回值。

>>> def square(x):
        return mul(x, x)
>>> def square(x):
        return mul(x, x-1) + x

換句話說,函數定義應該能夠隱藏細節。函數的用戶可能不能自己編寫函數,但是可以從其它程序員那里獲得它作為“黑盒”。用戶不應該需要知道如何實現來調用。Python 庫擁有這個特性。許多開發者使用在這里定義的函數,但是很少有人看過它們的實現。實際上,許多 Python 庫的實現并不完全用 Python 編寫,而是 C 語言。

1.3.7 運算符

算術運算符(例如+-)在我們的第一個例子中提供了組合手段。但是我們還需要為包含這些運算符的表達式定義求值過程。

每個帶有中綴運算符的 Python 表達式都有自己的求值過程,但是你通常可以認為他們是調用表達式的快捷方式。當你看到

>>> 2 + 3
5

的時候,可以簡單認為它是

>>> add(2, 3)
5

的快捷方式。

中綴記號可以嵌套,就像調用表達式那樣。Python 運算符優先級中采用了常規的數學規則,它指導了如何解釋帶有多種運算符的復合表達式。

>>> 2 + 3 * 4 + 5
19

和下面的表達式的求值結果相同

>>> add(add(2, mul(3, 4)) , 5)
19

調用表達式的嵌套比運算符版本更加明顯。Python 也允許括號括起來的子表達式,來覆蓋通常的優先級規則,或者使表達式的嵌套結構更加明顯:

>>> (2 + 3) * (4 + 5)
45

和下面的表達式的求值結果相同

>>> mul(add(2, 3), add(4, 5))
45

你應該在你的程序中自由使用這些運算符和括號。對于簡單的算術運算,Python 在慣例上傾向于運算符而不是調用表達式。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/45485.html

相關文章

  • SICP Python 描述 第三章 計算機程序的構造和解釋 3.1 引言

    摘要:為通用語言設計解釋器的想法可能令人畏懼。但是,典型的解釋器擁有簡潔的通用結構兩個可變的遞歸函數,第一個求解環境中的表達式,第二個在參數上調用函數。這一章接下來的兩節專注于遞歸函數和數據結構,它們是理解解釋器設計的基礎。 3.1 引言 來源:3.1 Introduction 譯者:飛龍 協議:CC BY-NC-SA 4.0 第一章和第二章描述了編程的兩個基本元素:數據和函數之間的...

    v1 評論0 收藏0
  • SICP Python 描述 3.4 異常

    摘要:的最常見的作用是構造異常實例并拋出它。子句組只在執行過程中的異常產生時執行。每個子句指定了需要處理的異常的特定類。將強制轉為字符串會得到由返回的人類可讀的字符串。 3.4 異常 來源:3.4 Exceptions 譯者:飛龍 協議:CC BY-NC-SA 4.0 程序員必須總是留意程序中可能出現的錯誤。例子數不勝數:一個函數可能不會收到它預期的信息,必需的資源可能會丟失,或者網...

    pkhope 評論0 收藏0
  • SICP Python 描述 2.6 實現類和對象

    摘要:以這種方式實現對象系統的目的是展示使用對象隱喻并不需要特殊的編程語言。我們的實現并不遵循類型系統的明確規定。反之,它為實現對象隱喻的核心功能而設計。是分發字典,它響應消息和。 2.6 實現類和對象 來源:2.6 Implementing Classes and Objects 譯者:飛龍 協議:CC BY-NC-SA 4.0 在使用面向對象編程范式時,我們使用對象隱喻來指導程序...

    chenjiang3 評論0 收藏0
  • SICP Python 描述 1.2 編程元素

    摘要:程序用于在編程社群的成員之間交流這些想法。在編程中,我們處理兩種元素函數和數據。在中,我們可以使用賦值語句來建立新的綁定,它包含左邊的名稱和右邊的值。例如,它并不能處理賦值語句。這些圖解的必要部分是函數的表示。 1.2 編程元素 來源:1.2 The Elements of Programming 譯者:飛龍 協議:CC BY-NC-SA 4.0 編程語言是操作計算機來執行任務...

    CoorChice 評論0 收藏0
  • SICP Python 描述 1.4 實踐指南:函數的藝術

    摘要:實踐指南函數的藝術來源譯者飛龍協議函數是所有程序的要素,無論規模大小,并且在編程語言中作為我們表達計算過程的主要媒介。目前為止,我們討論了函數的形式特性,以及它們如何使用。第一行描述函數的任務。 1.4 實踐指南:函數的藝術 來源:1.4 Practical Guidance: The Art of the Function 譯者:飛龍 協議:CC BY-NC-SA 4.0 函...

    lemon 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<