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实现在每个独立进程中运行一个函数的方法
Apr 23 Python
Python socket编程实例详解
May 27 Python
Django中使用celery完成异步任务的示例代码
Jan 23 Python
python输入整条数据分割存入数组的方法
Nov 13 Python
Python学习笔记之lambda表达式用法详解
Aug 08 Python
python golang中grpc 使用示例代码详解
Jun 03 Python
在tensorflow实现直接读取网络的参数(weight and bias)的值
Jun 24 Python
地图可视化神器kepler.gl python接口的使用方法
Dec 22 Python
python 如何上传包到pypi
Dec 24 Python
Python关于拓扑排序知识点讲解
Jan 04 Python
Python 中random 库的详细使用
Jun 03 Python
PYTHON InceptionV3模型的复现详解
May 06 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
什么是调频(FM)、调幅(AM)、短波(SW)、长波(LW)
2021/03/01 无线电
phpExcel导出大量数据出现内存溢出错误的解决方法
2013/02/28 PHP
php中通过数组进行高效随机抽取指定条记录的算法
2013/09/09 PHP
zf框架的Filter过滤器使用示例
2014/03/13 PHP
PHP模板引擎Smarty的缓存使用总结
2014/04/24 PHP
php解析json数据实例
2014/08/19 PHP
抛弃 PHP 代价太高
2016/04/26 PHP
thinkphp在php7环境下提示Cannot use ‘String’ as class name as it is reserved的解决方法
2016/09/30 PHP
PHP迭代器接口Iterator用法分析
2017/12/28 PHP
JavaScript高级程序设计(第3版)学习笔记13 ECMAScript5新特性
2012/10/11 Javascript
JS实现图片高亮展示效果实例
2015/11/24 Javascript
JS实现的表格行鼠标点击高亮效果代码
2015/11/27 Javascript
JS采用绝对定位实现回到顶部效果完整实例
2016/06/20 Javascript
用jquery快速解决IE输入框不能输入的问题
2016/10/04 Javascript
BootStrap实现带关闭按钮功能
2017/02/15 Javascript
pace.js和NProgress.js两个加载进度插件的一点小总结
2018/01/31 Javascript
微信小程序使用websocket通讯的demo,含前后端代码,亲测可用
2019/05/22 Javascript
解决vue-photo-preview 异步图片放大失效的问题
2020/07/29 Javascript
[02:55]2018DOTA2国际邀请赛勇士令状不朽珍藏Ⅲ饰品一览
2018/08/01 DOTA
python3编码问题汇总
2016/09/06 Python
Python实现简单的多任务mysql转xml的方法
2017/02/08 Python
Django错误:TypeError at / 'bool' object is not callable解决
2019/08/16 Python
django中的数据库迁移的实现
2020/03/16 Python
python torch.utils.data.DataLoader使用方法
2020/04/02 Python
CSS3 Media Queries(响应式布局可以让你定制不同的分辨率和设备)
2013/06/06 HTML / CSS
巴基斯坦电子产品购物网站:Home Shopping
2017/09/14 全球购物
百度软件工程师职位
2013/02/14 面试题
会计出纳岗位职责
2013/12/25 职场文书
外贸英语专业求职信范文
2013/12/25 职场文书
大学生联谊活动策划书(光棍节)
2014/10/10 职场文书
2014年反腐倡廉工作总结
2014/12/05 职场文书
个人求职意向书
2015/05/11 职场文书
青春雷锋观后感
2015/06/10 职场文书
企业培训简报范文
2015/07/20 职场文书
logback 实现给变量指定默认值
2021/08/30 Java/Android
【海涛教你打DOTA】剑圣第一人称视角解说
2022/04/01 DOTA