关于Python函数的基础
Python的函数是第一类对象
Python所定义的函数属于第一类对象,那什么是第一类对象呢?
“第一类对象”这一名称最早由克里斯托弗·斯特雷奇在1960年代发明。第一类对象(first-class object)与面向对象编程(oriented-object programming)中的概念并没有什么严密的关系,它可以指程序中的任何实体。一般第一类对象所特有的特性为:
- 可以将其存入变量或其他数据结构
- 可以将其作为参数传递给别的函数
- 可以将其作为函数的返回值
- 可以在执行期间创造,而无需完全在设计期全部写出
- 即使没有被系结至某一变量或名称,也可以存在
绝大多数语言中,数值与基础型别都是第一类对象,然而不同语言中对函数的区别很大,例如C语言与C++中的函数不是第一类对象,因为在这些语言中函数不能在执行期创造,而必须在设计时全部写好。
掌握这一概念有助于lambda
,decorator
以及函数式编程
的理解和使用。为了一一说明Python中所定义的函数所具有的第一类对象特性,接下来所使用的例子都将以yell
这个函数为基础进行拓展。
1 | def yell(text): |
函数即是对象
在Python中,一切皆对象。字符串,列表,模块等都是对象,函数也不例外。
yell既然作为python中的对象,就可以将其赋值给另一个变量。
1 | bark = yell |
这里赋值语句进行的操作是为函数创建另一个引用名称,两个引用名称指向的是内存中的同一块地址。函数对象本身和函数名(或者说函数的引用)可以看做一个事物内部两个独立的部分,我们可以用语句删除其中一个yell
,而bark
仍然指向之前定义的函数,bark
可以正常调用。
1 | del yell |
函数可以存入其他数据结构中
1 | funcs = [bark, str.lower, str.capitalize] |
访问存储在列表中的函数对象与访问列表元素并无二致。甚至可以直接通过索引访问到函数。
1 | for f in funcs: |
函数对象本身作为参数传递给别的函数
1 | def greet(func): |
我们定义了greet
函数,greet
以其他函数的引用作为参数,并在greet
内部调用其他函数,并打印函数调用后的结果。
1 | 'hello', 'hey', 'hi'])) list(map(bark, [ |
以其他函数为参数的函数又称为高阶函数,Python
内建函数中最典型的高阶函数就是map
,map
接收一个函数引用和一个可迭代的参数列表,返回函数在每个参数上的掉调用结果。
嵌套函数
顾名思义,嵌套函数即在函数内部再次定义了函数,而通常情况下,外部函数会返回内部函数的引用,或者返回调用内部函数后的结果。
1 | def speak(text): |
聪明如你,容易看出内部函数的生存期仅限于当我们调用外部函数时。
1 | 'Yo') whisper( |
内部函数无法直接访问,除非我们在外部函数块中返回内部函数的引用。
1 | def get_speak_func(volume): |
函数get_speak_func
并未调用任何内部函数,它仅仅根据参数volumn
大小返回指定的某个内部函数,即返回了某种行为或者动作,而非内容。既然返回了某个函数,就可以直接调用这个函数,或者将其赋值给变量。
1 | 0.3) get_speak_func( |
稍作总结,如果函数的定义是为了实现某种功能,那么别的函数可以通过参数传递的方法获得这种功能,也可以将其定义在自己内部返回这项功能。
函数可以记录局部状态
所谓记录局部状态,就是处于外部函数作用域中的变量,可以被内部函数捕捉到。将之前的get_speak_func
函数稍作修改:
1 | def get_speak_func(text,volume): |
注意到内部函数whisper
和yell
并没有接受任何参数,但是却可以使用外部函数的参数text
。这种内部函数可以使用外部函数参数的行为称作语法闭包(注意:这里内部只能使用外部函数的参数变量,而不能使用外部函数块内定义的变量)。语法闭包有什么用处呢?之前提到过,嵌套函数可以用来返回某种行为或功能,而语法闭包则可以让程序对这种行为或功能进行多种配置。在下面这个例子中,利用make_adder
可以制造多个加法函数。
1 | def make_adder(n): |
OPP中的对象也能像函数那样使用
所有的函数都是对象,但是反之不一定成立。OPP中的对象并非函数,但是通过定义__call__(self,)
,却可以使其像函数那样可调用(callable)。将对象实例作为函数调用,会自动调用函数__call__
。我们可以通过函数callable
检查某个对象是否可以调用。
1 | class Adder(): |
总结
- 一切皆对象,函数也不例外。我们可以将其赋值给某个变量,将其存储在某种数据结构中,将其传递给其他函数,或从其他函数返回。
- 利用第一类对象函数,我们可以抽象出,并传递某种行为或功能。
- 函数也可以嵌套,内部函数可以记录外部函数参数的状态,此种用法成为闭包。
- 对象也可以被调用,即可以像函数那样被调用。
Lambdas: 匿名函数
用关键字lambda
可以声明小型匿名函数,其用法和用def
声明的函数用法类似。但是,用lambda声明的函数可以不用和变量名绑定即可立即调用。
1 | lambda x, y: x+y add = |
参考资料
Python Tricks 3-1: Python’s Funstions Are First-Class