写在前面:Fluent Python 系列是学习《Fluent Python》的笔记 ~ 这是一本适合Python进阶的书,不仅有一些高级语法,作者对部分底层实现也有讲解,以便于我们理解Python的设计,比如为什么普通字典是无序的呢?
更新记录
2021.10.12 函数装饰器(最初的需求:实现类似其他面向对象语言的函数重载)
2021.11.10 迭代器和生成器(同学问我的面试题:python3中range和python 2中xrange的区别)
目录
- 函数装饰器
- 1 变量作用域
- 2 闭包
- 3 装饰器
- 3.1 基本概念和写法
- 3.2 装饰器何时执行
- 3.3 标准库中常用的两个装饰器函数
- 3.4 参数化装饰器
- 4 小结
- 迭代器和生成器
- 1 序列可迭代
- 2 可迭代对象和迭代器
- 3 生成器
- 3.1 什么是生成器
- 3.2 生成器函数
- 3.3 生成器表达式
- 3.4 标准库中常用的生成器函数
- 4 小结
函数装饰器
1 变量作用域
注意函数体内赋值的局部变量与函数体外同名全局变量

2 闭包

3 装饰器
3.1 基本概念和写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34import time from clockedeco import clock from functools import lru_cache @clock def snooze(seconds): time.sleep(seconds) #@clock #def factorial(n): # return 1 if n < 2 else factorial(n-1) def factorial(n): return 1 if n < 2 else factorial(n-1) factorial = clock(factorial) @lru_cache() @clock def fibonacci(n): if n < 2 : return n else: return fibonacci(n-2) + fibonacci(n-1) if __name__ == '__main__': # print('*' * 40, 'Calling snooze(.123)') # snooze(.123) # 此时调用的已经是被装饰过的函数 # print('*' * 40, 'Calling factorial(6)') # print('6!=', factorial(6)) print(fibonacci(6))
3.2 装饰器何时执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25registry = [] # 定义装饰器 def register(func): print('running register(%s)' % func) registry.append(func) return func # 定义被装饰的函数 @register def f1(): print('running f1()') def f3(): print('runing f3()') def main(): print('running main()') print('registry->', registry) f1() # 调用被装饰的函数 f3() if __name__ == '__main__': main()
3.3 标准库中常用的两个装饰器函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27from functools import singledispatch from collections import abc import numbers import html @singledispatch def htmlize(obj): """处理Object类型的基函数""" content = html.escape(repr(obj)) return '<pre>{}</pre>'.format(content) # 以下各个专门函数使用@《base_function》.register()装饰 @htmlize.register(str) def _(text): # 专门函数的名称无关紧要 content = html.escape(text).replace('n', '<br>n') return '<p>{0}</p>'.format(content) @htmlize.register(numbers.Integral) def _(n): return '<pre>{0} (0x{0:x})</pre>'.format(n) @htmlize.register(tuple) @htmlize.register(abc.MutableSequence) def _(seq): inner = '</li>n<li>'.join(htmlize(item) for item in seq) return '<ul>n<li>' + inner + '</li>n</ul>'
3.4 参数化装饰器

4 小结
我所用到的函数装饰器的场景:
-
优化递归函数:跟书中介绍的例子相同,斐波那契数列有一种解法便称之为“备忘录”,就是将已经计算出的结果保存在数组中,eg:保存f(1), f(2),这样在计算f(3)=f(1)+f(2)的时候就不需要再重复计算f(1)和f(2),只需要在数组中查找即可。Python内嵌的装饰器实现了备忘录的功能,不需要手动再设置数组保存结果,直接使用@lru_cache装饰器即可。
https://leetcode-cn.com/problems/fibonacci-number/
2.使用Python实现类似C++/Java 函数重载的功能
Python确实没有函数重载,遇到针对不同情况调用不同函数的时候,最简单的方法就是使用if-elif-else分别调用专门的函数,这种做法的扩展性确实不好。
迭代器和生成器
小插曲:同学面试腾讯,面试官的一个问题是:Python3的range()和Python2的xrange()区别是什么?然后我第一次认真审视了“迭代器和生成器”这两个概念hhh,之后在自己的程序中也多次用到,今天系统回顾一下这部分的内容。
1 序列可迭代
在Python中,所有的集合都是可以迭代的,迭代器用于支持:
- for循环
- 构建和扩展集合类型
- 逐行遍历文本文件
- 列表推导、字典推导、集合推导
- 元组拆包
- 函数调用时,使用*拆包参数

2 可迭代对象和迭代器

Example 1:Sentence类用于生成可迭代对象;SentenceIterator类用于生成迭代器对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33import re # 使Python具有正则表达式功能 import reprlib RE_WORD = re.compile('w+') # 用于匹配一个或者多个 字母、数字、下划线 class Sentence: def __init__(self, text): self.text = text self.words = RE_WORD.findall(text) # 返回正则表达式非重叠匹配的字符串列表 def __repr__(self): return 'Sentence(%s)' % reprlib.repr(self.text) # 生成简略字符串表示形式 def __iter__(self): return SentenceIterator(self.words) # 实例化并返回迭代器 class SentenceIterator: def __init__(self, words): self.words = words self.index = 0 def __next__(self): try: word = self.words[self.index] except IndexError: raise StopIteration() self.index += 1 return word def __iter__(self): return self # 返回迭代器对象本身
Example 2:更加符合Python风格的写法,使用生成器函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import re # 使Python具有正则表达式功能 import reprlib RE_WORD = re.compile('w+') # 用于匹配一个或者多个 字母、数字、下划线 class Sentence: def __init__(self, text): self.text = text self.words = RE_WORD.findall(text) # 返回正则表达式非重叠匹配的字符串列表 def __repr__(self): return 'Sentence(%s)' % reprlib.repr(self.text) # 生成简略字符串表示形式 def __iter__(self): # 使用了yield关键字-->生成器函数 for word in self.words: yield word return
3 生成器
3.1 什么是生成器

3.2 生成器函数

1
2
3
4
5
6
7
8
9
10
11# 生成器函数 def gen_AB(): print('start') yield 'A' print('continue') yield 'B' print('end') for c in gen_AB(): print('-->', c)
3.3 生成器表达式

1
2
3
4
5
6
7
8
9
10# 生成器表达式和列表推导 # 列表推导-->迫切迭代gen_AB()生成的生成器对象产出的元素 res1 = [x*3 for x in gen_AB()] print(res1) # 生成器表达式-->生成器对象 这里gen_AB()并没有真正执行 res2 = (x*3 for x in gen_AB()) for i in res2: # for 循环迭代隐式调用next(res2),gen_AB()才会真正执行 print('-->', i)
3.4 标准库中常用的生成器函数

4 小结
1.迭代器和生成器其实在很多应用场景下被隐式的创建和调用,比如for循环中,以及上面提到的range();关于二者的关系,实际的应用环境中其实也不会严格的区分。

2.这部分的核心内容:Python是从可迭代对象中创建迭代器的,那什么是可迭代对象呢?我们认为实现了可以返回迭代器对象的__iter__()或者__getitem__()的对象为可迭代对象。进而__iter__()返回的迭代器对象又是什么样子的呢?要求迭代器对象必须实现__next__()和__iter__()这两个方法,分别用于返回下一个元素和对象本身。不过在Python中,经常使用生成器来完成迭代器对象的功能,而生成器对象是由生成器函数或者生成器表达式返回的,使用yiled关键字的函数就是生成器函数。
最后
以上就是优美季节最近收集整理的关于Fluent Python | 函数装饰器、迭代器和生成器的全部内容,更多相关Fluent内容请搜索靠谱客的其他文章。
发表评论 取消回复