绑定

细心的读者可能记得我在 第 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 "", line 1,

in

?

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 风格”传递一个连续。但若是更深一层理解,函数和面向对象编程大部分都是这样。

原文:http://www.jb51.net/article/63953.htm

python函数的特性_深入Python函数编程的一些特性相关推荐

  1. python函数定义错误_[转载]python之函数的使用及异常处理2021.1.30

    2.1 定义函数 def 函数名(参数): 代码1 代码2 ...... 2.2 调⽤函数 函数名(参数) 注意: 1. 不同的需求,参数可有可⽆. 2. 在Python中,函数必须先定义后使⽤. 2 ...

  2. python函数应用实例_【Python 第22课】 函数应用示例

    前两课稍稍介绍了一下函数,但光说概念还是有些抽象了,今天就来把之前那个小游戏用函数改写一下. 我希望有这样一个函数,它比较两个数的大小. 如果第一个数小了,就输出"too small&quo ...

  3. python宝石与石头_学习python (2)

    Python 2.7.x 与 Python 3.x 的不同点 本文翻译自:<Key differences between Python 2.7.x and Python 3.x> 许多 ...

  4. 关于python中lambda函数的描述_关于Python中的lambda函数

    lambda是Python编程语言中使用频率较高的一个关键字.那么,什么是lambda?它有哪些用法?网上的文章汗牛充栋,可是把这个讲透的文章却不多.这里,我们通过阅读各方资料,总结了关于Python ...

  5. python怎么理解函数的参数_理解Python中函数的参数

    定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了.对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解 ...

  6. python中函数的调用_慢步python,编程中函数的概念,python中函数的声明和调用

    函数,曾经是一个很高大尚的概念.笔者是在高中数学里认识的函数,先是从y=2x+3 这条代数式开始的.y是因变量,x是自变量,y因为x取值的变化而变化. 再后来式子变成这样:f(x)=2x+3,f(x) ...

  7. python整数转换字符串_使用Python中的str()函数将整数值转换为字符串

    python整数转换字符串 Given an integer value and we have to convert the value to the string using str() func ...

  8. python小老鼠编程_成都python函数学习教程,Python编写课程

    1.urllib2/urllib实现urllib2和urllib是Python中的两个内置模块,要实现HTTP功能,实现方式是以urllib2为主,urllib为辅.1.1首先实现一个完整的请求与响应 ...

  9. python绝对值函数偏码_绝对值python

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 试图建立关分拣python字典,我将如何去打印基于该值的绝对值排序顺序pytho ...

最新文章

  1. 在线作图|2分钟绘制一张豆荚图
  2. 2019年Q4中国云市场报告:阿里腾讯百度位居前三
  3. Mysql_多表查询练习
  4. WSE2.0中X509安全令牌的使用
  5. linux终端<Terminal>使用ping
  6. 基本的Material Design布局结构
  7. Python包管理器-pip
  8. echarts 不支持 手机 浏览器_中国北斗卫星导航系统真的来了!获国产手机力挺:但iPhone却不支持...
  9. 使用nrm管理npm源的切换
  10. PreScan快速入门到精通第三讲快速搭建第一个自动驾驶仿真模型
  11. 基于51单片机的智能时控开关设计
  12. 找不到移动硬盘解决办法
  13. Navicat远程连接Oracle数据库
  14. Android:从源码剖析Hander机制
  15. 用MMD做mmd动态模型
  16. 腾达ac1200远端服务器无响应,连3个磊科MG1200ac必死机
  17. [bzoj3698]XWW的难题——有上下界的最大流
  18. 【原创】Firda+typescript 接收WX消息
  19. 系统运维系列 之在Windows下可以查看文件夹大小的工具TreeSize Free
  20. SSD接口详解,再也不会买错固态硬盘了

热门文章

  1. 利用Matplotlib绘制莫比乌斯带
  2. 神经网络加深和加宽的影响
  3. c语言acii 码转化为字母,C语言字符转换ASCII码
  4. 命令提示符的三种打开方式
  5. CAD室内设计构思怎么写?
  6. SAP中采购计划行中的原有数量的分析实例
  7. php 区位码字符,PHP汉字转区位码用法示例
  8. Java 处理那种无法解析的json数组
  9. 基于wxpy的微信防撤回
  10. 《人工智能》机器学习 - 第5章 逻辑回归(二 二元逻辑回归实战)