Python之路(四)——Python 三器(迭代器、生成器、装饰器)
本节内容
- 迭代器
- 生成器
- 装饰器
一、迭代器(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 迭代器
- 可迭代对象:实现了__iter__方法;迭代器:实现了__next__方法
- 可以被
next()函数调用并不断返回下一个值的对象称为迭代器(Iterator),直到没有数据时抛出StopIteration错误。不断next取下一个值的数据流对象。可以看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。 - 迭代器一定是可迭代对象,但可迭代对象不一定是迭代器。如:[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")
装饰特性总结:
- 是高阶函数(有函数作为参数、返回值)
- 有函数嵌套
浙公网安备 33010602011771号