larken

勤奋的人生才有价值

导航

第5章 一等函数

#《流畅的Python》读书笔记
# 第三部分 把函数视作对象
# 第5章 一等函数
#在Python中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程序实体:
    #在运行时创建
    #能赋值给变量或数据结构中的元素
    #能作为参数传给函数
    #能作为函数的返回结果

# 5.1 把函数视作对象
# 示例 5-1 中的控制台会话表明,Python 函数是对象。这里我们创建了一个函数,然后调用它,读取它的 __doc__ 属性,并且确定函数对象本身是 function 类的实例。

#示例 5-1 创建并测试一个函数,然后读取它的 __doc__ 属性,再检查它的类型
def factorial(n):
'''return n!'''
return 1 if n<2 else n*factorial(n-1)
print(factorial(42)) #1405006117752879898543142606244511569936384000000000
print(factorial.__doc__) #return n!
print(type(factorial)) #<class 'function'>

# 示例 5-2 通过别的名称使用函数,再把函数作为参数传递
fact=factorial
print(fact) #<function factorial at 0x01C15420>
print(fact(5)) #120
print(map(factorial,range(11))) #<map object at 0x013D3710>
print(list(map(fact,range(11)))) #[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

# 5.2 高阶函数
# 接受函数为参数,或者把函数作为结果返回的函数是高阶函数(higher-order function)。

# 示例 5-3 根据单词长度给一个列表排序
fruits=['strawberry','fig','apple','cherry','raspberry','banana']
print(sorted(fruits,key=len))

# 示例 5-4 根据反向拼写给一个单词列表排序
def reverse(word):
return word[::-1]
print(reverse('testing')) #gnitset
print(sorted(fruits,key=reverse)) #['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

# map、filter和reduce的现代替代品
# 函数式语言通常会提供 map、filter 和 reduce 三个高阶函数(有时使用不同的名称)。
# 在 Python 3 中,map 和 filter 还是内置函数,但是由于引入了列表推导和生成器表达式,它们变得没那么重要了。
# 列表推导或生成器表达式具有 map 和 filter 两个函数的功能,而且更易于阅读,如示例 5-5 所示。

# 示例 5-5 计算阶乘列表:map和filter与列表推导比较
print(list(map(fact,range(6)))) #[1, 1, 2, 6, 24, 120]
print([fact(n) for n in range(6)]) #[1, 1, 2, 6, 24, 120]
print(list(map(factorial, filter(lambda n: n % 2, range(6))))) #[1, 6, 120]
print([factorial(n) for n in range(6) if n % 2]) #[1, 6, 120]

# 示例 5-6 使用reduce和sum计算0~99之和
from functools import reduce
from operator import add
print(reduce(add,range(100))) #4950
print(sum(range(100))) #4950
# 函数式语言通常会提供map、filter和reduce三个高阶函数(有时使用不同的名称)。
# 在Python3中,map和filter还是内置函数,但是由于引入了列表推导和生成器表达式,它们变得没那么重要了。

# 5.3 匿名函数
# lambda 关键字在 Python 表达式内创建匿名函数。

# 示例 5-7 使用lambda表达式反转拼写,然后依此给单词列表排序
fruits=['strawberry','fig','apple','cherry','raspberry','banana']
print(sorted(fruits,key=lambda word:word[::-1])) #['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

# 5.4 可调用对象
# Python 中有各种各样可调用的类型,因此判断对象能否调用,最安全的方法是使用内置的callable()函数:
print(abs,str,13) #<built-in function abs> <class 'str'> 13
print([callable(obj) for obj in (abs,str,13)]) #[True, True, False]

# 5.5 用户定义的可调用类型
# 不仅Python函数是真正的对象,任何 Python 对象都可以表现得像函数。为此,只需实现实例方法 __call__

# 示例 5-8 bingocall.py:调用 BingoCage 实例,从打乱的列表中取出一个元素
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.pick() 的快捷方式是 bingo()。
bingo=BingoCage(range(3))
print(bingo.pick())
print(bingo.pick())
print(callable(bingo))

# 5.6 函数内省
# 除了 __doc__,函数对象还有很多属性。使用 dir 函数可以探知factorial 具有下述属性:

# 示例 5-9 列出常规对象没有而函数有的属性
class C:pass
obj=C()
def func():pass
print(sorted(set(dir(func))-set(dir(obj))))
# ['__annotations__', '__call__', '__closure__', '__code__', '__defaults__',
# '__get__', '__globals__', '__kwdefaults__', '__name__', '__qualname__']

# 5.7 从定位参数到仅限关键字参数
# Python 最好的特性之一是提供了极为灵活的参数处理机制,而且 Python3 进一步提供了仅限关键字参数(keyword-only argument)。
# 与之密切相关的是,调用函数时使用 * 和 **“展开”可迭代对象,映射到单个参数。

# 示例 5-10 tag 函数用于生成 HTML 标签;使用名为 cls 的关键字参数传入“class”属性,这是一种变通方法,因为“ class”是 Python的关键字

# 示例 5-11 tag 函数(见示例 5-10)众多调用方式中的几种

# 5.8 获取关于参数的信息
# HTTP 微框架 Bobo 中有个使用函数内省的好例子。
# 示例 5-12 是对 Bobo教程中“Hello world”应用的改编,说明了内省怎么使用。

# 示例 5-12 Bobo 知道 hello 需要 person 参数,并且从 HTTP 请求中获取它

# 示例 5-13 如果请求中缺少函数的参数,Bobo 返回 403 forbidden响应;curl -i 的作用是把首部转储到标准输出

# 示例 5-14 传入所需的 person 参数才能得到 OK 响应

# 示例 5-15 在指定长度附近截断字符串的函数

# 示例 5-16 提取关于函数参数的信息

# 示例 5-17 提取函数的签名

# 示例 5-18 把tag 函数(见示例 5-10)的签名绑定到一个参数字典上

# 5.9 函数注解
# Python 3 提供了一种句法,用于为函数声明中的参数和返回值附加元数据。
# 示例 5-19 是示例 5-15 添加注解后的版本,二者唯一的区别在第一行。

# 示例 5-19 有注解的 clip 函数

# 示例 5-20 从函数签名中提取注解

# 5.10 支持函数式编程的包
# 虽然 Guido 明确表明,Python 的目标不是变成函数式编程语言,但是得益于 operator 和 functools 等包的支持,函数式编程风格也可以信手拈来。
# 接下来的两节分别介绍这两个包。

# 5.10.1 operator模块
# 在函数式编程中,经常需要把算术运算符当作函数使用。例如,不使用递归计算阶乘。
# 求和可以使用 sum 函数,但是求积则没有这样的函数。我们可以使用 reduce 函数(5.2.1 节是这么做的),但是需要一个函数计算序列中两个元素之积。
# 示例 5-21 展示如何使用 lambda 表达式解决这个问题。

# 示例 5-21 使用reduce函数和一个匿名函数计算阶乘
from functools import reduce
def fact(n):
return reduce(lambda a,b:a*b,range(1,n+1))

# 示例 5-22 使用reduc和operator.mul函数计算阶乘
from functools import reduce
from operator import mul
def fact(n):
return reduce(mul,range(n,n+1))

# 示例 5-23 演示使用 itemgetter 排序一个元组列表

# 示例 5-24 定义一个 namedtuple,名为 metro_data
 
# 示例 5-25 methodcaller使用示例:第二个测试展示绑定额外参数的方式
from operator import methodcaller
s='The time has come'
upcase=methodcaller('upper')
print(upcase(s)) #THE TIME HAS COME
hiphenate=methodcaller('replace',' ','-')
print(hiphenate(s)) #The-time-has-come

# 5.10.2 使用functools.partial冻结参数
# functools 模块提供了一系列高阶函数,其中最为人熟知的或许是reduce,我们在 5.2.1 节已经介绍过。
# 余下的函数中,最有用的是partial 及其变体,partialmethod。

# 示例5-26使用partial把一个两参数函数改编成需要单参数的可调用对象
from operator import mul
from functools import partial
triple=partial(mul,3)
print(triple(7)) #21
print(list(map(triple,range(1,10)))) #[3, 6, 9, 12, 15, 18, 21, 24, 27]

# 示例 5-27 使用 partial 构建一个便利的 Unicode 规范化函数

# 示例 5-28 把 partial 应用到示例 5-10 中定义的 tag 函数上

# 5.11 本章小结

# 5.12 延伸阅读
# 接下来的两章继续探讨使用函数对象编程。第 6 章说明一等函数如何简化某些经典的面向对象设计模式,第 7 章说明函数装饰器(一种特别的高阶函数)和支持装饰器的闭包机制。

 

posted on 2019-03-17 23:43  larken  阅读(207)  评论(0编辑  收藏  举报