函数是一等对象的学习总结

函数是一等对象(first-class object)

在Python中,万物皆可为对象,函数也不例外。

在Python中,函数为一等对象(first-class object),那么什么才是一等对象呢?

编程语言理论家把“一等对象”定义为满足下述条件的程序实体

  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传给函数
  • 能作为函数的返回结果

1、把函数当作对象

Talk is cheap. Show me the code.

下面分别举例说明这四个特点

  • 在运行时创建

    test()
    
    
    def test():
        print('hello')
    

    Traceback (most recent call last):
    File “E:/pyproject/fluent_python/test1.py”, line 1, in
    test()
    NameError: name ‘test’ is not defined

    对于上述这段代码,我们可以看到程序会报错。

    这是因为Python是从上到下一行一行执行语句的。当我们在执行到第一行时,由于还没有执行到函数test定义这一部分。因此会报test没有定义这种错误。

    这一现象说明函数满足“在运行时创建”这一特点

  • 能赋值给变量或数据结构中的元素

    ⚠️注:对于定义的一个函数例如func,不加括号指的是函数的本身,加括号表示对函数的调用(原因后续会解释)。

    def func1():
        print('hello')
    
    
    def func2():
        print('world')
    
    # 赋值给变量
    a = func1  #建立了a对函数func1的一个引用
    a()
    a_list = [1, 2]
    # 赋值给列表中的元素
    a_list[1] = func2 
    print(a_list)
    

    hello
    [1, <function func2 at 0x0000026F5C2A60D8>]

  • 能作为参数传给函数

    def inner():
        print('hello')
    
    
    def outer(func):
        func()
    
    # 将函数inner作为参数赋值给outer
    outer(inner)
    

    hello

  • 能作为函数的返回结果

    def inner():
        print('hello')
    
    
    def outer(func):
    
        return func
    
    
    print(outer(inner))
    

    <function inner at 0x000001EA859461F8>

2、高阶函数

接受函数作为参数,或者把函数当做结果返回的函数是高阶函数(higher-order function)

例如,想要根据单词的长度排序,只需将len函数传入到sorted函数的key参数中.

fruits = ['cherry', 'apple', 'strawberry', 'banana']
sorted_fruits = sorted(fruits, key=len)
print(sorted_fruits)

[‘apple’, ‘cherry’, ‘banana’, ‘strawberry’]

高阶函数map,filter和reduce的现代替代品

reduce在Python3中已移除。map,filter可以使用列表推导或者生成器表达式代替,而且可读性更强。

def factorial(n):
    return 1 if n < 2 else n * factorial(n - 1)


print(list(map(factorial, range(6))))
print([factorial(n) for n in range(6)])

print(list(map(factorial, filter(lambda x: x % 2, range(6)))))
print([factorial(n) for n in range(6) if n % 2])

[1, 1, 2, 6, 24, 120]
[1, 1, 2, 6, 24, 120]
[1, 6, 120]
[1, 6, 120]

3、匿名函数

lambda关键词在Python表达式内创建匿名表函数。然而,Python 简单的句法限制了lambda 函数的定义体只能使用纯表达式。换句话说,lambda 函数的定义体中不能赋值,也不能使用while 和try 等Python 语句。

fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
sorted_fruits = sorted(fruits, key=lambda word: word[::-1])
print(sorted_fruits)

[‘banana’, ‘apple’, ‘fig’, ‘raspberry’, ‘strawberry’, ‘cherry’]

lambda 句法只是语法糖:与def 语句一样,lambda 表达式会创建函数对象

4、可调用对象

除了用户定义的函数,调用运算符(即()),还可以应用到其他对象上。

想判断对象能否调用,可以使用内置的callable函数。

Python 数据模型文档列出了7 种可调用对象

名称说明
用户定义的函数使用def或lambda表达式创建
内置函数如len 或time.strftime
内置方法如dict.get
方法在类的定义体中定义的函数
调用类时会运行类的__new__方法创建一个实例,然后运行__\init__方法,初始化实例,最后把实例返回给调用方。
类的实例如果类定义了__call__,那么他的实例可以作为函数调用
生成器函数使用yield关键词的函数或方法。调用生成器函数返回的是生成器对象

判断对象是否可调用callable

print([callable(obj) for obj in [abs, str, 13, list]])

函数和方法的区别

  • 函数指的是用户自己定义的函数或者内置的函数

    是可以以对象作为参数传入函数的

    # 将字符串对象作为参数传入函数中
    list('hello')
    
  • 方法指的是用户定义的类或者内置的类中定义的方法

    a_dict = {'a': 1, 'b': 2}
    # a_dict为一个dict类的实例
    print(type(a_dict)) # output:<class 'dict'>
    # 方法的调用形式:实例.方法
    print(a_dict.get('a'))
    

两者最大的使用区别就是

函数要作用在某个对象身上,形式为:function(obj)

方法需要使用到点(即.)操作,形式为:instance.method()

7、仅限关键词指定

下面函数展示了一个人能力和财富之间关系

def cal_money(name, *other, working_competence=0, **other_competence):
    money = working_competence*1000000
    print(f'{name} can earn ${money} in the end of the year')

例如,公司来了个小红,但是这个小红一天天不务正业,整天跟朋友一起玩,没事睡觉看电视。

今天主管说,经过精密的设计,公司开发了一块软件,通过这个软件发放年终奖

于是她想通过这个软件计算一下自己能拿多少年终奖。

他将她认识的朋友和消耗的时间都输入进去了

cal_money('xiaohong','xiaoming','xiaogang',watch_tv=100, sleep=200)

结果出来的结果让她十分伤心

xiaohong can earn $0 in the end of the year

为了测试这个功能是不是有问题,她特意找来了工作能力很强的小凤。小凤试了一下

cal_money('xiaofeng',working_competence=5)

xiaofeng can earn $5000000 in the end of the year

小红恍然大悟,原始在指定的方向努力才会有收获

这个故事告诉我们:working_competence只能通过关键字参数指定才能对其赋值,它一定不会捕获未命名的定位参数,如你认识的人xiaoming,也不会捕获关键字名不对应的值,如干与工作提升无关的事watch_tv

  • 定义函数时若想指定仅限关键字参数,要把它们放到前面有* 的参数后面。

    如将working_competence放到*other的后面。

  • 如果不想支持数量不定的定位参数,但是想支持仅限关键字参数,在签名中放一个*

    例如

    def new(name, *, working_competence):
        money = working_competence * 1000000
        print(f'{name} can earn ${money} in the end of the year')
    

    在这个函数中,要求你只能填两个值(例如:绩效考核时,我并不想知道你认识多少人),而且我不管你第一个值输入什么,你第二个值必须体现出你的工作能力working_competence=4

    new('a',working_competence=4)
    

    a can earn $4000000 in the end of the year

参考文献

参考内容

[1] Fluent Python by Luciano Ramalho (O’Reilly).Copyright 2015 Luciano Ramalho, 978-1-491-94600-8.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值