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在多玩图片上下载妹子图的实现代码
Aug 13 Python
wxPython中listbox用法实例详解
Jun 01 Python
python编程开发之类型转换convert实例分析
Nov 13 Python
Python实现网站注册验证码生成类
Jun 08 Python
Django视图之ORM数据库查询操作API的实例
Oct 27 Python
Opencv+Python 色彩通道拆分及合并的示例
Dec 08 Python
Python图像处理之gif动态图的解析与合成操作详解
Dec 30 Python
树莓派实现移动拍照
Jun 22 Python
Python基于pygame实现单机版五子棋对战
Dec 26 Python
Python的logging模块基本用法
Dec 24 Python
Pytorch 如何加速Dataloader提升数据读取速度
May 28 Python
详解Python中的for循环
Apr 30 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获取post中的json数据的实现方法
2011/06/08 PHP
30个最好的jQuery 灯箱插件分享
2011/04/25 Javascript
IE6下focus与blur错乱的解决方案
2011/07/31 Javascript
在javascript中执行任意html代码的方法示例解读
2013/12/25 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
浅谈node.js中async异步编程
2015/10/22 Javascript
javascript正则表达式定义(语法)总结
2016/01/08 Javascript
AngualrJS中的Directive制作一个菜单
2016/01/26 Javascript
js 动态添加元素(div、li、img等)及设置属性的方法
2016/07/19 Javascript
很棒的js Tab选项卡切换效果
2016/08/30 Javascript
100多个基础常用JS函数和语法集合大全
2017/02/16 Javascript
使用FileReader API创建Vue文件阅读器组件
2018/04/03 Javascript
vue中v-cloak解决刷新或者加载出现闪烁问题(显示变量)
2018/04/20 Javascript
vue.js与element-ui实现菜单树形结构的解决方法
2018/04/21 Javascript
微信小程序实现倒计时补零功能
2018/07/09 Javascript
VUE 配置vue-devtools调试工具及安装方法
2018/09/30 Javascript
echarts多条折线图动态分层的实现方法
2019/05/24 Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
2019/06/03 Javascript
Js图片点击切换轮播实现代码
2020/07/27 Javascript
Jquery 获取相同NAME 或者id删除行操作
2020/08/24 jQuery
express异步函数异常捕获示例详解
2020/11/30 Javascript
[01:31]DOTA2上海特级锦标赛 SECRET战队完整宣传片
2016/03/16 DOTA
python在linux中输出带颜色的文字的方法
2014/06/19 Python
在Python中处理XML的教程
2015/04/29 Python
详解Python的Django框架中的模版相关知识
2015/07/15 Python
Python编程使用*解包和itertools.product()求笛卡尔积的方法
2017/12/18 Python
Selenium+Python 自动化操控登录界面实例(有简单验证码图片校验)
2019/06/28 Python
django 通过url实现简单的权限控制的例子
2019/08/16 Python
详解Java中一维、二维数组在内存中的结构
2021/02/11 Python
Html5 Canvas 实现一个“刮刮乐”游戏
2019/09/05 HTML / CSS
html5使用canvas实现跟随光标跳动的火焰效果
2014/01/07 HTML / CSS
如何获取某个日期是当月的最后一天
2013/12/05 面试题
What's the difference between an interface and abstract class? (接口与抽象类有什么区别)
2012/10/29 面试题
大学新生军训自我鉴定范文
2014/09/13 职场文书
承诺书模板大全
2015/05/04 职场文书
美丽的大脚观后感
2015/06/03 职场文书