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程序设计入门(3)数组的使用
Jun 16 Python
python中的函数用法入门教程
Sep 02 Python
Python中利用函数装饰器实现备忘功能
Mar 30 Python
Python中selenium实现文件上传所有方法整理总结
Apr 01 Python
python正则表达式的使用
Jun 12 Python
Python绘制KS曲线的实现方法
Aug 13 Python
Python实现的字典排序操作示例【按键名key与键值value排序】
Dec 21 Python
Python根据欧拉角求旋转矩阵的实例
Jan 28 Python
Pycharm+Python+PyQt5使用详解
Sep 25 Python
Python 元组拆包示例(Tuple Unpacking)
Dec 24 Python
opencv 图像腐蚀和图像膨胀的实现
Jul 07 Python
Python模块zipfile原理及使用方法详解
Aug 04 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 缓存函数代码
2008/08/27 PHP
php 上传功能实例代码
2010/04/13 PHP
PHP截取IE浏览器并缩小原图的方法
2016/03/04 PHP
浅谈PHP发送HTTP请求的几种方式
2017/07/25 PHP
ThinkPHP 在阿里云上的nginx.config配置实例详解
2017/10/11 PHP
Mootools 1.2教程 正则表达式
2009/09/15 Javascript
分享一个自己写的table表格排序js插件(高效简洁)
2011/10/29 Javascript
解析DHTML,JavaScript,DOM,BOM以及WEB标准的描述
2013/06/19 Javascript
node.js中的fs.fchownSync方法使用说明
2014/12/16 Javascript
jQuery插件uploadify实现ajax效果的图片上传
2016/06/18 Javascript
Vue实现购物车功能
2017/04/27 Javascript
JS实现微信里判断页面是否被分享成功的方法
2017/06/06 Javascript
vue 实现全选全不选的示例代码
2018/03/29 Javascript
vue keep-alive请求数据的方法示例
2018/05/16 Javascript
Vue实现左右菜单联动实现代码
2018/08/12 Javascript
基于Koa2写个脚手架模拟接口服务的方法
2018/11/27 Javascript
JavaScript如何使用插值实现图像渐变
2020/06/28 Javascript
vue实现购物车列表
2020/06/30 Javascript
[04:54]DOTA2-DPC中国联赛1月31日Recap集锦
2021/03/11 DOTA
Python使用pymysql小技巧
2017/06/04 Python
浅谈Python中的作用域规则和闭包
2018/03/20 Python
tensorflow 用矩阵运算替换for循环 用tf.tile而不写for的方法
2018/07/27 Python
Python 读写文件的操作代码
2018/09/20 Python
Flask核心机制之上下文源码剖析
2018/12/25 Python
python实现获取单向链表倒数第k个结点的值示例
2019/10/24 Python
Python turtle库绘制菱形的3种方式小结
2019/11/23 Python
python中Django文件上传方法详解
2020/08/05 Python
Python CategoricalDtype自定义排序实现原理解析
2020/09/11 Python
5个你不知道的HTML5的接口介绍
2013/08/07 HTML / CSS
美国豪华的多品牌精品店:The Webster
2019/07/31 全球购物
迪斯尼假期(欧洲、中东及非洲):Disney Holidays EMEA
2021/02/15 全球购物
课程设计的心得体会
2014/09/03 职场文书
五好家庭事迹材料
2014/12/20 职场文书
2015年网管个人工作总结
2015/05/22 职场文书
《刷子李》教学反思
2016/02/20 职场文书
会议主持词通用版
2019/04/02 职场文书