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 os模块中的isfile()和isdir()函数均返回false问题解决方法
Feb 04 Python
Python实现二叉堆
Feb 03 Python
Python设计模式之备忘录模式原理与用法详解
Jan 15 Python
详解Python下载图片并保存本地的两种方式
May 15 Python
python-pyinstaller、打包后获取路径的实例
Jun 10 Python
python 表格打印代码实例解析
Oct 12 Python
Python字符串中删除特定字符的方法
Jan 15 Python
django迁移文件migrations的实现
Mar 31 Python
Python3读取和写入excel表格数据的示例代码
Jun 09 Python
降低python版本的操作方法
Sep 11 Python
Python基础之hashlib模块详解
May 06 Python
使用qt quick-ListView仿微信好友列表和聊天列表的示例代码
Jun 13 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
一个MYSQL操作类
2006/11/16 PHP
解析Linux下Varnish缓存的配置优化
2013/06/20 PHP
PHP的基本常识小结
2013/07/05 PHP
php页面防重复提交方法总结
2013/11/25 PHP
php实现的SSO单点登录系统接入功能示例分析
2016/10/12 PHP
php使用crypt()函数进行加密
2017/06/08 PHP
js传值 判断
2006/10/26 Javascript
火狐浏览器(firefox)下获得Event对象以及keyCode
2008/11/13 Javascript
比较全面的event对像在IE与FF中的区别 推荐
2009/09/21 Javascript
写出高效jquery代码的19条指南
2014/03/19 Javascript
jquery 为a标签绑定click事件示例代码
2014/06/23 Javascript
js实现感应鼠标图片透明度变化的方法
2015/02/20 Javascript
JavaScript实现带标题的图片轮播特效
2015/05/20 Javascript
ES6新特性七:数组的扩充详解
2017/04/21 Javascript
javascript用rem来做响应式开发
2018/01/13 Javascript
关于微信小程序bug记录与解决方法
2018/08/15 Javascript
对vue下点击事件传参和不传参的区别详解
2018/09/15 Javascript
vue中使用axios post上传头像/图片并实时显示到页面的方法
2018/09/27 Javascript
Vue核心概念Getter的使用方法
2019/01/18 Javascript
python通过colorama模块在控制台输出彩色文字的方法
2015/03/19 Python
一个基于flask的web应用诞生 组织结构调整(7)
2017/04/11 Python
详解python tkinter教程-事件绑定
2019/03/28 Python
Python 变量的创建过程详解
2019/09/02 Python
基于Python的OCR实现示例
2020/04/03 Python
Django限制API访问频率常用方法解析
2020/10/12 Python
HTML5 背景的显示区域实现
2020/07/09 HTML / CSS
加拿大著名时装品牌:SOIA & KYO
2016/08/23 全球购物
奇怪的鱼:Weird Fish
2018/03/18 全球购物
GC是什么?为什么要有GC?
2013/12/08 面试题
高中校园广播稿
2014/01/11 职场文书
五年级科学教学反思
2014/02/05 职场文书
乡镇遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
2014年组织委员工作总结
2014/12/01 职场文书
销售人员管理制度
2015/08/06 职场文书
有趣的二维码:使用MyQR和qrcode来制作二维码
2021/05/10 Python
vue中data里面的数据相互使用方式
2022/06/05 Vue.js