Python3 中作为一等对象的函数解析


Posted in Python onDecember 11, 2019

Python3 函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

在 Python 语言中,函数与整数、字符串、字典等基本数据类型一样,都是 一等对象 。所谓一等对象,即满足如下三个条件:

  • 在运行时创建
  • 能赋值给变量
  • 能作为函数的参数或返回值

以下 IDLE 中的代码即在运行时创建了函数 factorial :

>>> def factorial(n):
...   '''calculates n!'''
...   return 1 if n < 2 else n * factorial(n-1)
...
>>> factorial(5)
120
>>> factorial.__doc__
'calculates n!'
>>> type(factorial)
<class 'function'>
>>> fact = factorial
>>> fact
<function factorial at 0x7f55bc771c10>
>>> fact(5)
120
>>> list(map(fact, range(10)))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

从输出中可以看出, factorial 是 function 类的实例对象, __doc__ 是 factorial 对象众多属性中的一个。

可以把 factorial 函数赋值给变量 fact ,通过 fact 变量调用 factorial 函数。还可以把 factorial 作为参数传递给 map 函数。

这些行为表现了函数作为一等对象的特性。

一、高阶函数

接受函数作为参数,或者把函数作为返回值的函数即为 高阶函数 。

如内置用于排序的 sorted 函数,它的 key 参数用于传入一个函数,在需要排序的每个元素上执行特定的操作。如根据单词长度对多个字符串进行排序:

>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
>>> sorted(fruits, key=len)
['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']

任何单参数的函数都可以作为 key 的值传给 sorted 函数,如把单词反向拼写作为排序条件:

>>> def reverse(word):
...   return word[::-1]
...
>>> reverse('test')
'tset'
>>> sorted(fruits, key=reverse)
['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

map、filter 与 reduce

函数式编程语言通常会提供 map 、 filter 和 reduce 三个高阶函数或者实现了类似功能的函数。Python3 中的列表推导和生成器即具有 map 和 filter 函数的功能。

参考如下示例:

>>> def fact(n):
...   return 1 if n < 2 else n * fact(n-1)
...
>>> list(map(fact, range(6)))
[1, 1, 2, 6, 24, 120]
>>> [fact(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> list(map(fact, filter(lambda n: n % 2, range(6))))
[1, 6, 120]
>>> [fact(n) for n in range(6) if n % 2]
[1, 6, 120]

通过列表推导可以完成与 map 或 filter 函数类似的工作,且可读性更高,也避免了使用 lambda 表达式。

reduce 在 Python2 中是内置函数,但在 Python3 中被移到了 functools 模块中。 reduce 可以把某个操作连续地应用到某个序列上,累计所有的结果,把产生的一系列值规约成一个值。因此常用于求和计算,但内置的 sum 函数在可读性和性能方面更优。

>>> from functools import reduce
>>> from operator import add
>>> reduce(add, range(101))
5050
>>> sum(range(101))
5050

二、匿名函数

可以使用 lambda 关键字在 Python 表达式内创建匿名函数。

在函数的参数列表中最适合使用匿名函数。如前面的根据字符串反序后的结果对单词列表进行排序,可以使用 lambda 匿名函数替代传入 sorted 的 reverse 函数:

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

lambda 表达式 lambda words: words[::-1] 即等同于之前的 reverse 函数:

def reverse(word):
   return word[::-1]

除了作为参数传给某个高阶函数外,Python 很少使用匿名函数。

三、可调用对象

除了用户自定义的函数,其他可调用对象也可以使用调用运算符(即 () )。

Python 的数据模型中共包含 7 种可调用对象:

  • 用户自定义函数:使用 def 语句或 lambda 表达式创建的函数
  • 内置函数:由 C 语言(CPython)实现的函数,如 len 或 time.strftime 等
  • 内置方法:使用 C 语言实现的方法,如 dict.get
  • 方法:在类的定义体中定义的函数
  • 类:类在调用时会使用 __new__ 方法创建实例,然后运行 __init__ 初始化实例,最后将实例返回给调用方。调用类相当于调用函数。
  • 类的实例:如果类的定义中实现了 __call__ 方法,则其实例可以作为函数调用
  • 生成器:使用 yield 关键字的函数或方法。可以返回生成器对象。

使用内置的 callable() 函数可以确认对象是否可调用。

任何 Python 对象都可以表现得像函数,只需实现该实例的 __call__ 方法。

如下面的 bingocall.py ,从列表中随机取出一个元素:

import random
class BingoCage:
  def __init__(self, items):
    self._items = list(items)
    random.shuffle(self._items)
  def pick(self):
    try:
      return self._items.pop()
    except IndexError:
      raise LookupError('pick from empty BingoCage')
  def __call__(self):
    return self.pick()
bingo = BingoCage(range(50))
print(bingo.pick())
# => 38
print(bingo())
# => 22
print(callable(bingo))
# => True

bingo 是 BingoCage 类的一个实例,由于 BingoCage 类中实现了 __call__ 方法,则 bingo 对象是可调用的( bingo() )。

四、支持函数式编程的模块

operator

在函数式编程中,经常需要将算术运算符当作函数使用。如不使用递归计算阶乘。

使用 reduce 和 lambda 表达式计算阶乘:

>>> from functools import reduce
>>> def fact(n):
...   return reduce(lambda a, b: a*b, range(1, n+1))
...
>>> fact(5)
120

Python 中的 operator 为多个运算符提供了对应的函数,可以避免写 lambda a, b: a*b 这种匿名函数。

使用 reduce 和 operator.mul 计算阶乘:

>>> from operator import mul
>>> from functools import reduce
>>> def fact(n):
...   return reduce(mul, range(1, n+1))
...
>>> fact(5)
120

operator 模块中还有一类 itemgetter 和 attrgetter 函数,可以替代从序列中取出元素或读取属性的 lambda 表达式。

如根据元组中的第二个元素对多个元组进行排序:

>>> metro_data = [
...   ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
...   ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
...   ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
...   ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
...   ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
... ]
>>> from operator import itemgetter
>>> for city in sorted(metro_data, key=itemgetter(1)):
...   print(city)
...
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833))
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))
('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
('Mexico City', 'MX', 20.142, (19.433333, -99.133333))
('New York-Newark', 'US', 20.104, (40.808611, -74.020386))

如果把多个参数传递给 itemgetter ,则该函数会返回由提取的值构成的元组:

>>> cc_name = itemgetter(1, 0)
>>> for city in metro_data:
...   print(cc_name(city))
...
('JP', 'Tokyo')
('IN', 'Delhi NCR')
('MX', 'Mexico City')
('US', 'New York-Newark')
('BR', 'Sao Paulo')

attrgetter 与 itemgetter 作用类似,可以根据名称提取对象的属性。

operator 模块中还有一个 methodcaller 函数,可以用来在某个对象上调用由参数指定的方法。

>>> from operator import methodcaller
>>> s = 'The time has come'
>>> upcase = methodcaller('upper')
>>> upcase(s)
'THE TIME HAS COME'
>>> hiphenate = methodcaller('replace', ' ', '-')
>>> hiphenate(s)
'The-time-has-come'
functools.partial

高阶函数 functools.partial 用来 部分应用 某个函数。即基于某个函数创建一个新的可调用对象,并把原函数的某些参数固定。

如使用 partial 把一个接受双参数的函数改编成单参数的可调用对象:

>>> from operator import mul
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(7)
21
>>> list(map(triple, range(1, 10)))
[3, 6, 9, 12, 15, 18, 21, 24, 27]

partial() 函数返回一个 functools.partial 对象,该对象提供对原函数的访问和固定原函数参数的行为。

>>> def greeting(words, name):
...   return f'{words}, {name}!'
...
>>> from functools import partial
>>> greeting2 = partial(greeting, name='skitar')
>>> greeting2("what's up")
"what's up, skitar!"
>>> greeting2
functools.partial(<function greeting at 0x7f70f31788b0>, name='skitar')

总结

以上所述是小编给大家介绍的Python3 中作为一等对象的函数解析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
Python爬取附近餐馆信息代码示例
Dec 09 Python
Pandas 数据处理,数据清洗详解
Jul 10 Python
Python从文件中读取数据的方法讲解
Feb 14 Python
Pycharm 文件更改目录后,执行路径未更新的解决方法
Jul 19 Python
使用OpenCV实现仿射变换—旋转功能
Aug 29 Python
Python使用matplotlib绘制Logistic曲线操作示例
Nov 28 Python
python的列表List求均值和中位数实例
Mar 03 Python
python继承threading.Thread实现有返回值的子类实例
May 02 Python
sklearn的predict_proba使用说明
Jun 28 Python
Ubuntu20.04环境安装tensorflow2的方法步骤
Jan 29 Python
python爬虫scrapy基本使用超详细教程
Feb 20 Python
Python3接口性能测试实例代码
Jun 20 Python
opencv3/C++图像像素操作详解
Dec 10 #Python
Pandas时间序列重采样(resample)方法中closed、label的作用详解
Dec 10 #Python
Python3的unicode编码转换成中文的问题及解决方案
Dec 10 #Python
用OpenCV将视频分解成单帧图片,图片合成视频示例
Dec 10 #Python
python3 webp转gif格式的实现示例
Dec 10 #Python
Spring Cloud Feign高级应用实例详解
Dec 10 #Python
flask 使用 flask_apscheduler 做定时循环任务的实现
Dec 10 #Python
You might like
我用php+mysql写的留言本
2006/10/09 PHP
thinkphp备份数据库的方法分享
2015/01/04 PHP
php简单实现屏蔽指定ip段用户的访问
2015/04/29 PHP
PHP7+Nginx的配置与安装教程详解
2016/05/10 PHP
PHP中实现中文字串截取无乱码的解决方法
2018/05/29 PHP
PHP+swoole+linux实现系统监控和性能优化操作示例
2019/04/15 PHP
基于JQuery制作的产品广告效果
2010/12/08 Javascript
别了 JavaScript中的isXX系列
2012/08/01 Javascript
iframe子页面与父页面在同域或不同域下的js通信
2014/05/07 Javascript
Javascript中innerHTML用法实例分析
2015/01/12 Javascript
纯javascript版日历控件
2016/11/24 Javascript
jquery实现异步加载图片(懒加载图片一种方式)
2017/04/24 jQuery
关于vue-resource报错450的解决方案
2017/07/24 Javascript
原生js实现获取form表单数据代码实例
2019/03/27 Javascript
javascript实现图片轮播代码
2019/07/09 Javascript
vue 开发之路由配置方法详解
2019/12/02 Javascript
node.js使用mongoose操作数据库实现购物车的增、删、改、查功能示例
2019/12/23 Javascript
深入浅析vue全局环境变量和模式
2020/04/28 Javascript
js实现抽奖功能
2020/11/24 Javascript
[05:08]顺网杯ISS-DOTA2赛歌 少女偶像Lunar青春演绎
2013/12/05 DOTA
[04:47]DOTA2-潍坊风行电子俱乐部探秘
2014/08/08 DOTA
[04:15]DOTA2-DPC中国联赛1月19日Recap集锦
2021/03/11 DOTA
自动化Nginx服务器的反向代理的配置方法
2015/06/28 Python
Python中查看文件名和文件路径
2017/03/31 Python
python实现彩色图转换成灰度图
2019/01/15 Python
python3 下载网络图片代码实例
2019/08/27 Python
SneakerStudio英国:最佳运动鞋商店
2019/05/22 全球购物
80后婚前协议书范本
2014/10/24 职场文书
合作协议书范本
2014/10/25 职场文书
2015年银行柜员工作总结报告
2015/04/01 职场文书
天那边观后感
2015/06/09 职场文书
婚宴父亲致辞
2015/07/27 职场文书
2016年“我们的节日·重阳节”主题活动总结
2016/04/01 职场文书
2016年第十九届推普周活动总结
2016/04/06 职场文书
JS轻量级函数式编程实现XDM三
2022/06/16 Javascript
create-react-app开发常用配置教程
2022/06/25 Javascript