转:https://foofish.net/function-is-first-class-object.html
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
python 的函数是第一对象
正确理解 python 函数,能够帮我们更好地理解 装饰器(@)、匿名函数(lambda)与函数式编程等高阶技术。
一、函数是对象
在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
简单的函数:
>>> def foo(text):
... return len(text)
...
>>> foo('zen of python')
13
函数 foo() 作为一个对象,拥有对象模型的三个通用属性:id、类型、值:
>>> id(foo)
34515536
>>> type(foo)
<class 'function'>
>>> foo
<function foo at 0x020EAA50>
函数作为对象,可以赋值给另外一个变量:
>>> bar = foo
>>> bar
<function foo at 0x020EAA50>
>>> bar("zen of python")
13
>>> bar == foo
True
bar = foo
是因为他们指向一样
赋值给另外一个变量时,函数并不会被调用,仅仅是在函数对象上绑定一个新的名字而已(或新名字指向该函数),单纯的起一个新名字不需要加括号和参数;
二、函数可以存储在容器
容器对象(list、dict、set等)中可以放任何对象,包括证书、字符串。函数也可以放在容器对象中:
>>> funcs = [foo, str, len]
>>> funcs
[<function foo at 0x103f45e18>, <class 'str'>, <built-in function len>]
>>> for f in funcs:
... print(f("hello"))
...
5
hello
5
>>>
foo 是我们自定义的函数,str 和 len 是两个内置函数。可以通过索引或列表遍历来调用函数:
[<function foo at 0x020EAA50>, <class 'str'>, <built-in function len>]
>>> funcs[0]('zen of python')
13
三、函数可以作为参数
函数还可以作为参数传递给另外一个函数:
>>> def show(func):
... size = func("zen of python")
... print('length of string is : {}'.format(size))
...
>>> show(foo)
length of string is : 13
四、函数还可以作为返回值:
>>> def nick():
... return foo
...
>>> nick
<function nick at 0x020EA078>
>>> a = nick
>>> a
<function nick at 0x020EA078>
>>> a("python")
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: nick() takes 0 positional arguments but 1 was given
>>> a = nick()
>>> a
<function foo at 0x020EAA50>
>>> a("python")
6
这里有个错误,见最后
还可以简写为:
>>> nick()("python")
6
函数接收一个或多个函数作为输入或者函数输出(返回)的值是函数时,我们称这样的函数为高阶函数,show 和 nick 都属于高阶函数。
python 内置函数中,典型的高阶函数是 map,map 接收一个函数和一个迭代对象作为参数,调用 map 时,依次把迭代对象的元素作为参数调用该函数:
>>> map(foo, ['the', 'zen', 'of', 'python'])
<map object at 0x020D0250>
>>> lens = map(foo, ['the', 'zen', 'of', 'python'])
>>> list(lens)
[3, 3, 2, 6]
map 函数的作用相当于:
>>> [foo(i) for i in ['the', 'zen', 'of', 'python'] ]
[3, 3, 2, 6]
只不过 map 函数的效率更高一点
五、函数可以嵌套
python 还允许函数中定义函数,这种函数叫做嵌套函数:
>>> def get_length(text):
... def clean(t): # 2
... return t[1:]
... new_text = clean(text) # 1
... return len(new_text)
...
>>> get_length('python')
5
这个函数的目的是取出字符串的第一个字符后再计算它的长度。get_length 调用时,先执行 1 处的代码,发现有调用 clean 函数,接着执行 2 的代码,把返回值赋值给 new_text,再执行后续代码。
>>> clean("python")
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name 'clean' is not defined
函数中里面的嵌套函数不能在函数外面被调用,只有局部使用域。
六、实现了 call的类也可以作为函数
对于一个自定义的类,如果实现了 call 方法,那么该类的实例对象的行为就是一个函数,可以被调用:
>>> class ADD:
... def __init__(self, n):
... self.n = n
... def __call__(self, x):
... return self.n + x
...
>>> add = ADD(1)
>>> add(4)
5
>>> ADD(1)(4)
5
执行 add(4) 相当于调用 ADD.call(add, 4),self 就是实例对象 add,self
.n 等于 1,所以返回值是:1 + 4:
add(4)
||
Add(1)(4)
||
Add.__call__(add, 4)
确定对象是否可以调用,可以用内置函数 callable 来判断。
>>> callable(foo)
True
>>> callable(1)
False
>>> callable(int)
True
总结
python 中包含函数在内的一切皆为对象,函数作为第一类对象,支持赋值给变量,支持作为参数传递给其他函数,支持作为其他函数的返回值,支持函数的嵌套,实现了 call 方法的类实例对象也可以当做函数被调用。
有点不明白,调用函数和类,什么时候需要写 (),什么时候不需要写:
函数:
>>> def foo(text):
... return len(text)
...
>>> def nick():
... return foo
a = nick 调用的 是 nick 函数本身,有没有参数都可以这样写,相当于给函数起个别名;
b = nick() 调用的是 nick 函数的返回值(foo),nick(var) 函数必须写完整,不然无法返回值;
nick()(var)
:nick()
是foo
函数,nick()(var)
是函数foo
的返回值,等同于b(var)
;
类:
>>> class test(object):
... y = "me"
... def __init__(self):
... self.y = 'you'
...
>>> a = test
>>> a
<class 'test'>
>>> print(a.y)
me
>>> a.y
'me'
>>> b = test()
>>> b
<test object at 0x020D0E50>
>>> b.y
'you'
>>> print(b.y)
you
不带括号,调用的是类本身,没有执行 __init__
函数;带括号的实例化方法调用了 __init__()
函数,此时必须传入需要的参数;
函数带不带括号,决定了是调用当前函数,还是调用返回值
类带不带括号,决定了掉不掉用 __init__
方法