Python进阶——生成器、迭代器、装饰器

第一部分 生成器

ls = [i**2 for i in range(1, 1000001)]
for i in ls:
    pass
  • 缺点:占用大量内存
  • 生成器
    (1)采用惰性计算的方式
    (2)无需一次性存储海量数据
    (3)一边执行一边计算,只计算每次需要的值
    (4)实际上一直在执行next()操作,直到无值可取

1.1 生成器表达式

  • 海量数据,不需存储
squares = (i**2 for i in range(1000000))
for i in squares:
    pass
  • 求0~100的和
    无需显式存储全部数据,节省内存(生成器只占用很少的内存)
sum((i for i in range(101)))
5050

1.2 生成器函数——yield

  • 生成斐波那契数列
    数列前两个元素为1,1 之后的元素为其前两个元素之和
def fib(max):
    ls = []
    n, a, b = 0, 1, 1
    while n < max:
        ls.append(a)
        a, b = b, a + b
        n = n + 1
    return ls


print(fib(10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  • 构造生成器函数
    在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
def fib(max):
    n, a, b = 0, 1, 1
    while n < max:
        yield a
        a, b = b, a + b
        n = n + 1
        

print(fib(10))

for i in fib(10):
    print(i)
<generator object fib at 0x000001BE11B19048>
1
1
2
3
5
8
13
21
34
55

第二部分 迭代器

2.1 可迭代对象——Iterable

  • 可直接作用于for循环的对象统称为可迭代对象
  • 可以使用isinstance()判断一个对象是否是Iterable对象

2.1.1 列表、元组、字符串、字典、集合、文件、range()

from collections import Iterable

print(isinstance([1, 2, 3], Iterable))
print(isinstance({"name": "Sarah"}, Iterable))
print(isinstance('Python', Iterable))
True
True
True

2.1.2 生成器

  • 生成器不但可以用于for循环,还可以被next()函数调用
    直到没有数据可取,抛出StopIteration
from collections import Iterable


squares = (i**2 for i in range(5))
print(isinstance(squares, Iterable))

print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
True
0
1
4
9
16
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-63-cb2f72ae7043> in <module>
     10 print(next(squares))
     11 print(next(squares))
---> 12 print(next(squares))

StopIteration: 

2.2 迭代器——Iterator

  • 可以被next()函数调用并不断返回下一个值,直至没有数据可取的对象称为迭代器
  • 特点:
    没有长度
    不可索引
    不可存在计算(a in b)
    会被耗尽
  • 可以使用isinstance()判断一个对象是否是Iterator对象

2.2.1 生成器都是迭代器

from collections import Iterator

squares = (i**2 for i in range(5))
print(isinstance(squares, Iterator))
True

2.2.2 列表、元组、字符串、字典、集合不是迭代器

from collections import Iterator

print(isinstance([1, 2, 3], Iterator))
False
可以通过iter(Iterable)创建迭代器
from collections import Iterator

print(isinstance(iter([1, 2, 3]), Iterator))
True
  • for item in Iterable 等价于:
    先通过iter()函数获取可迭代对象Iterable的迭代器
    然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item
    当遇到StopIteration的异常后循环结束

2.2.3 zip enumerate 等itertools里的函数是迭代器

from collections import Iterator

x = [1, 2]
y = ["a", "b"]
print(zip(x, y))

for i in zip(x, y):
    print(i)
    
isinstance(zip(x, y), Iterator)
<zip object at 0x0000017678E2ED48>
(1, 'a')
(2, 'b')
True
from collections import Iterator

numbers = [1, 2, 3, 4, 5]
print(enumerate(numbers))    # 把位置与元素组合成元组

for i in enumerate(numbers):
    print(i)
    
print(isinstance(enumerate(numbers), Iterator))
<enumerate object at 0x0000017678E27EA8>
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
True

2.2.4 文件是迭代器

from collections import Iterator

with open("测试文件.txt", "r", encoding = "utf-8") as f:
    print(isinstance(f, Iterator))
True

2.2.5 迭代器是可耗尽的

squares = (i**2 for i in range(5))
for square in squares:
    print(square)

print('-----------------')

for square in squares:
    print(square)
0
1
4
9
16
-----------------

2.2.6 range()不是迭代器

from collections import Iterator

numbers = range(10)
print(isinstance(numbers, Iterator))
print(len(numbers))   # 有长度
print(numbers[0])     # 可索引
print(9 in numbers)   # 可存在计算

for number in numbers:# 不会被耗尽
    print(number)

print('------------------------')

for number in numbers:
    print(number)

next(numbers)         # 不可被next()调用
False
10
0
True
0
1
2
3
4
5
6
7
8
9
------------------------
0
1
2
3
4
5
6
7
8
9
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-62-17f2e64f1f80> in <module>
     15     print(number)
     16 
---> 17 next(numbers)         # 不可被next()调用

TypeError: 'range' object is not an iterator
  • 可以称range()为懒序列
    它是一种序列
    但并不包含任何内存中的内容
    而是通过计算来回答问题

第三部分 装饰器

3.1 需求的提出

(1)需要对已开发上线的程序添加某些功能
(2)不能对程序中函数的源代码进行修改
(3)不能改变程序中函数的调用方式

  • 比如说,要统计每个函数的运行时间
def f1():
    pass


def f2():
    pass


def f3():
    pass


f1()
f2()
f3()

3.2 一个简单的装饰器

3.2.1 闭包实现

import time


def timer(func):
    
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
    return inner


def f1():
    print("f1 run")
    time.sleep(1)


f1 = timer(f1)             # 包含inner()和timer的环境,如传递过来的参数func
f1()
inner run
f1 run
f1 函数运行用时1.00秒
  • 上面的代码中,f1() 明显已经被偷换

3.2.2 语法糖——@修饰函数名

import time


def timer(func):
    
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
    return inner


@timer                      # 相当于实现了f1 = timer(f1)
def f1():
    print("f1 run")
    time.sleep(1)
    
    
f1()
inner run
f1 run
f1 函数运行用时1.00秒

3.3 装饰有参函数

import time


def timer(func):
    
    def inner(*args, **kwargs):    # 接受任意多的参数
        print("inner run")
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
    return inner


@timer                # 相当于实现了f1 = timer(f1)
def f1(n):
    print("f1 run")
    time.sleep(n)


f1(2)
inner run
f1 run
f1 函数运行用时2.00秒

3.4 装饰有返回值的函数

import time


def timer(func):
    
    def inner(*args, **kwargs):
        print("inner run")
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
        return res
    
    return inner


@timer                   # 相当于实现了f1 = timer(f1)
def f1(n):
    print("f1 run")
    time.sleep(n)
    return "wake up"


res = f1(2)
print(res)
inner run
f1 run
f1 函数运行用时2.00秒
wake up

3.5 带参数的装饰器

  • 装饰器本身要传递一些额外参数,对不同函数进行不同修饰
    需求:有时需要统计绝对时间,有时需要统计绝对时间的2倍
def timer(method):
    
    def outer(func):
    
        def inner(*args, **kwargs):
            print("inner run")
            if method == "origin":
                print("origin_inner run")
                start = time.time()
                res = func(*args, **kwargs)
                end = time.time()
                print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
            elif method == "double":
                print("double_inner run")
                start = time.time()
                res = func(*args, **kwargs)
                end = time.time()
                print("{} 函数运行双倍用时{:.2f}秒".format(func.__name__, 2*(end-start)))
            return res
    
        return inner
    
    return outer


@timer(method="origin")  # 相当于timer = timer(method = "origin")   f1 = timer(f1)
def f1():                # timer对应outer,f1对应inner
    print("f1 run")
    time.sleep(1)
    
    
@timer(method="double")
def f2():
    print("f2 run")
    time.sleep(1)


f1()
print()
f2()
inner run
origin_inner run
f1 run
f1 函数运行用时1.00秒

inner run
double_inner run
f2 run
f2 函数运行双倍用时2.00秒
  • 理解闭包是关键!!!

3.6 何时执行装饰器

  • 一装饰就执行,不必等调用
func_names=[]


def find_function(func):
    print("run")
    func_names.append(func)
    return func


@find_function
def f1():
    print("f1 run")
    

@find_function
def f2():
    print("f2 run")
    

@find_function
def f3():
    print("f3 run")


for func in func_names:    # 没有调用,func_names已经捕获到名字,还可以进行执行
    print(func.__name__)
    func()
    print()
run
run
run
f1
f1 run

f2
f2 run

f3
f3 run

3.6 回归本源

  • f1被偷换成了inner
    原函数的属性被掩盖了
import time

def timer(func):
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
    return inner

@timer                # 相当于实现了f1 = timer(f1)
def f1():
    time.sleep(1)
    print("f1 run")

print(f1.__name__) 
inner

语法糖——@wraps(参数函数名)

import time
from functools import wraps

def timer(func):
    @wraps(func)    # 属性仍然是原来的属性
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
    return inner

@timer                # 相当于实现了f1 = timer(f1)
def f1():
    time.sleep(1)
    print("f1 run")

print(f1.__name__) 
f1()
f1
inner run
f1 run
f1 函数运行用时1.00秒
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值