风清扬

导航

Python之路(四)——Python 三器(迭代器、生成器、装饰器)

本节内容

  1. 迭代器
  2. 生成器
  3. 装饰器

一、迭代器(Iterator)

可迭代对象(Iterable Object)

  • 表面来看,只要可以用 for...in...进行遍历元素的对象就是可迭代对象
  • 语法层面,如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象
from collections import Iterable

# 判断一个对象是可迭代对象
l = [1, 23]
d = {'name': 'alex', 'age': 12}
t = (1, 3, 4, 5, 7, 3)
set1 = set(t)
s = "1234343"

l_iter = [l, d, t, set1, s]
# 判断方法1
for obj in l_iter:
    print(isinstance(obj, Iterable))

# 判断方法2
for obj in l_iter:
    if '__iter__' in dir(obj):
        print(obj)


class MyList(object):
    def __init__(self):
        self.items = []

    def add(self, val):
        self.items.append(val)


    def __iter__(self):
        myiterator = MyIterator(self)
        return myiterator


class MyIterator(object):

    def __init__(self, mylist):
        self.mylist = mylist
        self.current = 0

    def __next__(self):
        if self.current < len(self.mylist.items):
            item = self.mylist.items[self.current]
            self.current += 1
            return item
        else:
            raise StopIteration

    def __iter__(self):
        return self


if __name__ == '__main__':
    mylist = MyList()
    mylist.add(1)
    mylist.add(2)
    mylist.add(3)
    mylist.add(4)
    mylist.add(5)
    print(isinstance(mylist,Iterable)) #True
# 判断方法3 可以使用for ...in ..

可迭代对象 vs 迭代器

  1. 可迭代对象:实现了__iter__方法;迭代器:实现了__next__方法
  2. 可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator),直到没有数据时抛出StopIteration错误。不断next取下一个值的数据流对象。可以看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
  3. 迭代器一定是可迭代对象,但可迭代对象不一定是迭代器。如:[1,3,4] 可以迭代,但不是迭代器

 二、生成器(Generator)

定义

特殊的迭代器,仅保存计算规则不提前生成结果,需要(next方法调用)时进行计算生成

#生成器
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],需求把列表里的每个值加1
a = [i for i in range(10)]
b = [i+1 for i in a]
print(b) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

#求0,100000w个数,每个数的平方是多少?
# a = [i**2 for i in range(1000000000)] #内存要爆炸

#使用生成器
g = (i**2 for i in range(1000000000)) #[] 变 ()
for i in range(10):
    #取前100 的计算
    print(next(g))  

两种生成器形式

  • 生成器表达式:上述
  • 生成器函数
# #使用生成器
# g = (i**2 for i in range(1000000000)) #[] 变 ()
# for i in range(10):
#     #取前100 的计算
#     print(next(g))

#生成器函数

#老方法:

# def fib(max_num):
#     n, a, b = 0, 0, 1
#     res = []
#     while n < max_num:
#         res.append(b)
#         a, b = b, a + b
#         n = n + 1
#     return res
#
# #求10、15位斐波那契数列
# a =fib(10)
# b = fib(15)
# print(a)
# print(b)

#问题: 求10000w 之前的斐波那契数列
#方法1: 修改代码,取1w 保存存列表,之后保存文件,循环处理

# 方法2:生成器,即:生成斐波那契数列与保存数据的逻辑 解耦

def fib(max_num):
    n, a, b = 0, 0, 1
    while n < max_num:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'


g_fib = fib(100000000)
l = []
# 1000行进行操作
# for i in range(1000):
#     #取值
#     next(g_fib)
#     #操作

# 需求二、取100..110位的数
for i in range(110):
    print(next(g_fib))
    if i >= 100-1:
        l.append(next(g_fib))
    else:
        #不做处理
        pass

print(l)

 三、装饰器(Wrapper)

什么是装饰器以及有什么用

  • 场景一
# def say_hello():
#     print("hello!")


#需求一:给打印格式调整: [DEBUG]: Enter say_hello()

# def say_hello():
#     print("[DEBUG]: Enter say_hello()")
#     print("hello!") 
  • 场景二
#需求二:这里有200个这样的函数,怎么做?
#方法一: 不睡觉,一个个添加
#方法二: 找到需要修改的文件;匹配对应行,修改之后加入写入文件
# def debug():
#     import inspect
#     caller_name = inspect.stack()[1][3]
#     print("[DEBUG]: enter {}()".format(caller_name))
#
# def say_hello():
#     debug()
#     print("hello!")
# 遍历文件
# def all_files():
#     #找到所有此类函数的文件
  • 场景三
# 需求三、不能修改源代码,风险评估太大
#方法:
# def wrapper_fun(f):
#     print("[DEBUG]: Enter %s()" %f.__name__)
#     f()


# 方法:  修改所有调用函数的地方:f() -> wrapper(f)

# if __name__ == '__main__':
#     #在每个调用处修改
#     wrapper_fun(say_hello)
# 问题:mmp,好多地方前都是直接写函数名调用的。
  • 场景四
def debug(f):
    def wrapper():
        #需要包裹的内容
        print("[DEBUG]: Enter %s()" %f.__name__)
        return f()
    return wrapper

def say_hello():
    print("hello!")

say_hello = debug(say_hello)


if __name__ == '__main__':
    #不用在调用处修改
    say_hello()

被装饰函数含有参数

#原函数
def say_hello(name,age=18):
    print("hello! %s ,your age:%s。" %(name,age))

#装饰器
def log(f):
    def wrapper(*args, **kwargs):
        print("调用前")
        f(*args, **kwargs)
        print("调用后")
    return wrapper

say_hello = log(say_hello) #自动完成参数传递

say_hello("alex")
# 调用前
# hello! alex ,your age:18。
# 调用后

装饰函数需要参数《终》

#简化代码
def log(error_level="Info"):
    def inner(f):
        def wrapper(*args, **kwargs):
            if error_level == "Error":
                print("记录日志")
            f(*args, **kwargs)
        return wrapper
    return inner

#原函数
@log("Error") # 相当于: inner = log("Error");say_hello = inner(say_hello)
def say_hello(name,age=18):
    print("hello! %s ,your age:%s。" %(name,age))


if __name__ == "__main__":
    say_hello("alex")

装饰特性总结:

  • 是高阶函数(有函数作为参数、返回值)
  • 有函数嵌套

posted on 2018-06-22 17:51  卜戈的博客  阅读(257)  评论(0编辑  收藏  举报