2 回答
TA贡献165条经验 获得超90个赞
>>> len(range(2)) 2 >>> a=len >>> a(range(2)) 2
每次看到别人问关于 函数传递,调用,返回等问题时,我就很开心,哈哈。 因为我也是从这里开始出发,迷上函数式编程的。感觉它比 OOP 更加抽象,也更加有意思。
好了不废话了, 开始解题。 首先说上面的代码: 第三行, 我们把len函数 赋值给了一个变量a, 接着在第四行,就可以调用函数a 了。 这说明函数本身和 Python 的其他对象一样(如,数字 1,2;字符串:'aa' 等)是可以作为值去赋值给其他变量名的, 同时 函数也就可以作为 另一个函数的返回值。
def make_adder(addend): def adder(augend): return augend + addend return adder p = make_adder(23) print (make_adder(23)) #(1) print (p) #(2) print (p(100)) #(3) print (p.__closure__) #(4) print (p.__closure__[0].cell_contents) #(5) ##下面是这5次 输出, 请自动修正我用的 Python3.x print() 语法 #(1) <function make_adder.<locals>.adder at 0x0000019AE2F2E950> #(2) <function make_adder.<locals>.adder at 0x0000019AE2F2E8C8> #(3) 123 #(4) (<cell at 0x0000019AE2BA8C18: int object at 0x000000006767F1F0>,) #(5) 23
好, 我们依次解释这五次输出,顺便讲下 几乎所有支持函数式编程的语言 都会有的概念: 闭包。这大概也是面试官最愿意问的问题吧:顺嘴一提,面试者随便一说,面试官点点头,能最大程度缓解气氛 是不? 哈哈, 反正我做面试的时候会问。
因为, make_adder 这个函数的返回值 是一个函数, 并且调用这个函数时,必须传递一个参数(后面这个参数的值,就成为了p 的闭包中很重要的组成部分)。所以第一个print 很好理解,就是一个函数体(把在函数中定义的函数 adder作为值 返回了)。
这第二个print 就和开篇的例子一个道理,讲一个函数作为值赋值给一个变量名了, 那么变量p 也是一个函数体。
至于第三个,就是对于函数p 的调用, 自然 打印的就是这个函数执行的结果,或者说是返回的结果。
好,从这里开始就是闭包了,闭包 和 词法作用域(lexical scoping)的概念,复制过来也就是几句简单但是不怎么容易理解的话, 这里就不赘述了,想了解请用搜索引擎。简单来说就是,当一个函数被定义时,它拥有的不仅仅是定义时的那些逻辑代码 还 拥有当时(定义时)的变量环境。 Python作为一个以优美著称的语言,必须 可以拿出解释这个概念的直观方法, 那就是每个函数 都有一个 闭包属性, 这第四次print 就是打印出了 函数的这个属性, 它是一个元组,其元素是 cell 类型。
第五次 print ,就很简单了, 就是打印出了, 函数的 闭包(tuple) 的第一个元素(cell)的内容(cell_contents)。
说了这么多, 也不知道题主理解了没有。总的来说, 正是由于 函数拥有闭包的这属性, 才能在 其定义的外部调用时, 不会忘记 或者找不到 其应该 附带的一些变量(此例中的 23)。
如果将函数改成:
def make_adder(addend): a = 100 def adder(augend): return augend + addend + a return adder
五次输出有什么不同呢? 请自行实验,对比,领悟 Python 中 函数的闭包属性。
TA贡献12条经验 获得超18个赞
1、return adder ,输出adder的内存地址
2、p = return adder , 同样是输出adder的内存地址
3、print make_adder(23)(100),调用adder函数返回23+100的值,这里23取的是make_adder函数的参数值,100是adder函数的参数值
添加回答
举报