首頁 後端開發 Python教學 深入Python函数编程的一些特性

深入Python函数编程的一些特性

Jun 10, 2016 pm 03:15 PM
python

绑定

细心的读者可能记得我在 第 1 部分的函数技术中指出的限制。特别在 Python 中不能避免表示函数表达式的名称的重新绑定。在 FP 中,名称通常被理解为较长表达式的缩写,但这一说法暗示着“同一表达式总是求出相同的值”。如果标记的名称重新被绑定,这一暗示便不成立。例如,让我们定义一些在函数编程中要用到的快捷表达式,比如:
清单 1. 以下 Python FP 部分的重新绑定要造成故障

>>> car = 
    
    lambda
    
     lst: lst[0]
>>> cdr = 
    
    lambda
    
     lst: lst[1:]
>>> sum2 = 
    
    lambda
    
     lst: car(lst)+car(cdr(lst))
>>> sum2(range(10))
1
>>> car = 
    
    lambda
    
     lst: lst[2]
>>> sum2(range(10))
5

登入後複製

不幸的是,完全相同的表达式 sum2(range(10)) 在程序中的两处求得两个不同的值,即使该表达式自身并没有在其参数中使用任何可变变量。

幸运的是, functional 模块提供了称为 Bindings 的类(向 Keller 提议)来防止这样的重新绑定(至少在偶然情况下,Python 不会阻止一心想要解除绑定的程序员)。然而使用 Bindings 需要一些额外的语法,这样意外就不太容易发生。在 functional 模块的示例中,Keller 将 Bindings 实例命名为 let (我假定在 ML 家族语言的 let 关键词的后面)。 例如,我们会这样做:
清单 2. 具有安全重新绑定的 Python FP 部分

>>> 
    
    from
    
     functional 
    
    import
    
     *
>>> let = Bindings()
>>> let.car = 
    
    lambda
    
     lst: lst[0]
>>> let.car = 
    
    lambda
    
     lst: lst[2]
Traceback (innermost last):
 File "<stdin>", line 1, 
    
    in
    
     &#63;
 File "d:\tools\functional.py", line 976, 
    
    in
    
     __setattr__
  
    
    raise
    
     BindingError, "Binding '%s' cannot be modified." % name
functional.BindingError: Binding 'car' cannot be modified.
>>> car(range(10))
0

登入後複製

很明显,真正的程序必须做一些设置来捕获“绑定错误”,而且他们被抛出也避免了一类问题的出现。

与 Bindings 一起, functional 提供 namespace 函数从 Bindings 实例中获取命名空间(实际上是个字典)。如果希望在 Bindings 中定义的(不可变)命名空间中运算一个表达式,这非常容易实现。Python 的 eval() 函数允许在命名空间中进行运算。 让我们通过一个示例来弄清楚:
清单 3. 使用不可变命名空间的 Python FP 部分

>>> let = Bindings()   
    
    # "Real world" function names
>>> let.r10 = range(10)
>>> let.car = 
    
    lambda
    
     lst: lst[0]
>>> let.cdr = 
    
    lambda
    
     lst: lst[1:]
>>> eval('car(r10)+car(cdr(r10))', namespace(let))
>>> inv = Bindings()   
    
    # "Inverted list" function names
>>> inv.r10 = let.r10
>>> inv.car = 
    
    lambda
    
     lst: lst[-1]
>>> inv.cdr = 
    
    lambda
    
     lst: lst[:-1]
>>> eval('car(r10)+car(cdr(r10))', namespace(inv))
17

登入後複製

闭包

FP 中有个有趣的概念 -- 闭包。实际上,闭包对许多开发人员都非常有趣,即使在如 Perl 和 Ruby 这样的无函数语言中也都包括闭包这一功能。而且,Python 2.1 目前正想加入词汇范围限制功能,这一功能将提供闭包的大部分功能。

什么 是闭包呢? Steve Majewski 最近在 Python 新闻组提供了对这一概念的很好描述:

对象是附带过程的数据……闭包是附带数据的过程。

闭包就象是 FP 的 Jekyll 对于 OOP 的 Hyde (角色或者也可能对调)。闭包类似对象示例,是一种将一大批数据和功能封装在一起的一种方式。

让我们回到先前的地方了解对象和闭包解决什么问题,同时了解一下问题如果没有这两样是如何解决的。函数返回的结果往往是由其计算中使用的上下文决定的。最常见的 -- 也可能是最明显的 -- 指定上下文的方法是向函数传递某些参数,通知函数处理什么值。但有时候“背景”和“前景”参数有着本质的区别 -- 在这特定时刻函数正在处理的和函数为多段潜在调用而“配置”之间的区别。

当把重点放在前景的时候,有许多处理背景的方法。其中一种是简单“咬出子弹”的方法,在每次调用的时候传递函数需要的每一个参数。这种方法通常在调用链中,只要在某些地方有可能需要值,就会传递一些值(或带有多成员的结构)。以下是一个小示例:
清单 4. 显示 cargo 变量的 Python 部分

>>> 
    
    defa
    
    (n):
...   add7 = b(n)
...   
    
    return
    
     add7
...
>>> 
    
    defb
    
    (n):
...   i = 7
...   j = c(i,n)
...   
    
    return
    
     j
...
>>> 
    
    defc
    
    (i,n):
...   
    
    return
    
     i+n
...
>>> a(10)   
    
    # Pass cargo value for use downstream
17

登入後複製

在 cargo 示例的 b() 中, n 除了起到传递到 c() 的作用外并无其他作用。另一种方法将使用全局变量:
清单 5. 显示全局变量的 Python 部分

>>> N = 10
>>> 
    
    defaddN
    
    (i):
...   
    
    global
    
     N
...   
    
    return
    
     i+N
...
>>> addN(7)  
    
    # Add global N to argument
17
>>> N = 20
>>> addN(6)  
    
    # Add global N to argument
26

全局变量 N 在任何希望调用 addN() 的时候起作用,但没有必要明确地传递全局背景“上下文”。另一个更 Python 专用的技术是将一个变量在定义时“冻结”入一个使用默认参数的函数:
清单 6. 显示冻结变量的 Python 部分

>>> N = 10
>>> 
    
    defaddN
    
    (i, n=N):
...   
    
    return
    
     i+n
...
>>> addN(5)  
    
    # Add 10
15
>>> N = 20
>>> addN(6)  
    
    # Add 10 (current N doesn't matter)
16

登入後複製

冻结变量本质上就是闭包。某些数据被“隶属”于 addN() 函数。对于完整的闭包,当定义 addN() 的时候,所有的数据在调用的时候都将可用。然而,在这个示例(或者许多更健壮的示例)中,使用默认的参数就能简单的够用了。 addN() 从未使用的变量并不会对其计算造成影响。

接着让我们来看一个更接近真实问题的 OOP 方法。年份的时间是我想起了那些“会见”风格的收集各种数据的税收程序 -- 不必有特定的顺序 -- 最终使用全部数据来计算。让我们创建一个简单的版本:
清单 7. Python 风格的税收计算类/示例

class
    
     TaxCalc:
  
    
    deftaxdue
    
    (self):
    
    
    return
    
     (self.income-self.deduct)*self.rate
taxclass = TaxCalc()
taxclass.income = 50000
taxclass.rate = 0.30
taxclass.deduct = 10000
    
    print
    
     "Pythonic OOP taxes due =", taxclass.taxdue()

登入後複製

在 TaxCalc 类(或其实例)中,能收集一些数据 -- 可以以任意顺序 -- 一旦获得了所需的所有元素,就能调用这一对象的方法来完成这一大批数据的计算。所有一切都在实例中,而且,不同示例携带不同的数据。创建多示例和区别它们的数据的可能性不可能存在于"全局变量"或"冻结变量"方法中。"cargo" 方法能处理这个问题,但对于扩展的示例来说,我们看到它可能是开始传递各种值的必要条件了。既然我们已讲到这,注意传递消息的 OPP 风格是如何处理的也非常有趣(Smalltalk 或 Self 与此类似,一些我使用的 OOP xBase 变量也是如此):
清单 8. Smalltalk 风格 (Python) 的税收计算

class
    
     TaxCalc:
  
    
    deftaxdue
    
    (self):
    
    
    return
    
     (self.income-self.deduct)*self.rate
  
    
    defsetIncome
    
    (self,income):
    self.income = income
    
    
    return
    
     self
  
    
    defsetDeduct
    
    (self,deduct):
    self.deduct = deduct
    
    
    return
    
     self
  
    
    defsetRate
    
    (self,rate):
    self.rate = rate
    
    
    return
    
     self
    
    print
    
     "Smalltalk-style taxes due =", \
   TaxCalc().setIncome(50000).setRate(0.30).setDeduct(10000).taxdue()

登入後複製

用每个 "setter" 来返回 self 使我们能把“现有的”东西看作是每个方法应用的结果。这与 FP 闭包方法有许多有趣的相似点。

有了 Xoltar 工具包,我们就能创建具有所期望的合并数据与函数特性的完整的闭包,同时还允许多段闭包(nee 对象)来包含不同的包:
清单 9. Python 函数风格的税收计算

from
    
     functional 
    
    import
    
     *
taxdue    = 
    
    lambda
    
    : (income-deduct)*rate
incomeClosure = 
    
    lambda
    
     income,taxdue: closure(taxdue)
deductClosure = 
    
    lambda
    
     deduct,taxdue: closure(taxdue)
rateClosure  = 
    
    lambda
    
     rate,taxdue: closure(taxdue)
taxFP = taxdue
taxFP = incomeClosure(50000,taxFP)
taxFP = rateClosure(0.30,taxFP)
taxFP = deductClosure(10000,taxFP)
    
    print
    
     "Functional taxes due =",taxFP()
    
    print
    
     "Lisp-style taxes due =", \
   incomeClosure(50000,
     rateClosure(0.30,
       deductClosure(10000, taxdue)))()

登入後複製

我们定义的每一个闭包函数都携带了函数范围内定义的任何值,然后将这些值绑定到函数对象的全局范围。然而,函数的全局范围看上去不必与实际模块的全局范围相同,同时与不同闭包的“全局”范围也不相同。闭包只是简单地“携带数据”。

在示例中,我们使用了一些特殊函数在闭包范围 (income、deduct、rate) 内放入了特定绑定。修改设计以在范围内放入任何绑定也非常简单。我们还可以在示例中使用具有细微差别的不同函数风格,当然这只是为了好玩。第一个成功的将附加值绑定入闭包范围内;使 taxFP 成为可变,这些“加入到闭包”的行可以任意顺序出现。然而,如果要使用如 tax_with_Income 这样的不可变名称,就必须将绑定行按照一定顺序排列,然后将前面的绑定传递到下一个。无论如何,一旦必需的一切被绑定入闭包的范围内,我们就调用 "seeded" 函数。

第二种风格看上去更接近 Lisp,(对我来说更像圆括号)。如果不考虑美观,第二种风格中发生了二件有趣的事情。第一件是名称绑定完全被避免了。第二种风格是一个单一表达式而不使用语句(请参阅 第 1 部分,讨论为什么这样会有问题)。

其它有关“Lisp 风格”闭包使用的有趣例子是其与上文提到的“Smalltalk 风格”消息传递方法有多少类似。两者累积了值和调用 taxdue() 函数/方法(如果没有正确的数据,两者在这些原始版本中都将报错)。“Smalltalk 风格”在每一步之间传递对象,而“Lisp 风格”传递一个连续。但若是更深一层理解,函数和面向对象编程大部分都是这样。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

vs code 可以在 Windows 8 中運行嗎 vs code 可以在 Windows 8 中運行嗎 Apr 15, 2025 pm 07:24 PM

VS Code可以在Windows 8上運行,但體驗可能不佳。首先確保系統已更新到最新補丁,然後下載與系統架構匹配的VS Code安裝包,按照提示安裝。安裝後,注意某些擴展程序可能與Windows 8不兼容,需要尋找替代擴展或在虛擬機中使用更新的Windows系統。安裝必要的擴展,檢查是否正常工作。儘管VS Code在Windows 8上可行,但建議升級到更新的Windows系統以獲得更好的開發體驗和安全保障。

visual studio code 可以用於 python 嗎 visual studio code 可以用於 python 嗎 Apr 15, 2025 pm 08:18 PM

VS Code 可用於編寫 Python,並提供許多功能,使其成為開發 Python 應用程序的理想工具。它允許用戶:安裝 Python 擴展,以獲得代碼補全、語法高亮和調試等功能。使用調試器逐步跟踪代碼,查找和修復錯誤。集成 Git,進行版本控制。使用代碼格式化工具,保持代碼一致性。使用 Linting 工具,提前發現潛在問題。

notepad 怎麼運行python notepad 怎麼運行python Apr 16, 2025 pm 07:33 PM

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。

vscode 擴展是否是惡意的 vscode 擴展是否是惡意的 Apr 15, 2025 pm 07:57 PM

VS Code 擴展存在惡意風險,例如隱藏惡意代碼、利用漏洞、偽裝成合法擴展。識別惡意擴展的方法包括:檢查發布者、閱讀評論、檢查代碼、謹慎安裝。安全措施還包括:安全意識、良好習慣、定期更新和殺毒軟件。

See all articles