函数

转: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 是因为他们指向一样

图片.png

赋值给另外一个变量时,函数并不会被调用,仅仅是在函数对象上绑定一个新的名字而已(或新名字指向该函数),单纯的起一个新名字不需要加括号和参数;

二、函数可以存储在容器

容器对象(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__ 方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343