可迭代对象
可迭代对象是迭代器、生成器、装饰器的基础,可迭代对象简单来说就是可以用for循环遍历的对象,比如常见的list,set和dict。
一、迭代器
对所有的可迭代对象调用 dir() 方法时,会发现他们都实现了 iter 方法。这样就可以通过 iter(object) 来返回一个迭代器。
a = 'abcdefg'
b = iter(a) # 创建迭代器对象
print(type(b))
# <class 'str_iterator'>
可以看到调用 iter() 之后,变成了一个 str_iterator 的对象。会发现增加了 __next__方法。所有实现了 __iter__和__next__两个方法的对象,都是迭代器。
迭代器是带状态的对象,它会记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。
# 迭代器
a = 'abcdefg'
b = iter(a) # 创建迭代器对象
print(type(b))
while True:
try:
print(next(b))
except StopIteration:
break
Python的for循环本质上就是通过不断调用next()函数实现的。
二、生成器
生成器就是含有yield关键字的函数,是一种用普通语法定义的迭代器。
# 生成器
def generator():
yield 'a'
yield 'b'
yield 'c'
g = generator()
print(type(g))
while True:
try:
print(next(g))
except StopIteration:
break
同样是通过 def 定义,然后通过 yield 来支持迭代器协议,所以比迭代器写起来更简单。
进行函数调用的时候,返回一个生成器对象。在使用 next() 调用的时候,遇到 yield 就返回,记录此时的函数调用位置,下次调用 next() 时,从断点处开始。
系统遇到 yield 关键词会将值返回,称为挂起,与return不同。
def gen(n):
while n > 0:
print('Before')
yield n
n-=1
print('After')
gg = gen(3)
while True:
try:
print(next(gg))
print('-------------')
except StopIteration:
break
输出结果:
Before
3
-------------
After
Before
2
-------------
After
Before
1
-------------
After
你完全可以像使用 iterator 一样使用 generator ,当然除了定义。定义一个iterator,你需要分别实现__iter__()方法和__next__()方法,但 generator 只需要一个小小的yield 。
generator 还有 send() 和 close() 方法,都是只能在next()调用之后,生成器出去挂起状态时才能使用的。
生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,此外,相比其它容器对象它更能节省内存和CPU,当然它可以用更少的代码来实现相似的功能。现在就可以动手重构你的代码了,但凡看到类似:
def something():
result = []
for ... in ...:
result.append(x)
return result
就可以用生成器函数来代替:
def iter_something():
for ... in ...:
yield x
提示:python 是支持协程的,也就是微线程,就是通过 generator 来实现的。配合 generator 我们可以自定义函数的调用层次关系从而自己来调度线程。
栗子:斐波那契数列
下面使用普通函数、迭代器、生成器来实现斐波那契数列
def fab(max):
x,y,z = 0, 0, 1
list = []
while x < max:
list.append(y)
y,z = z, x+y+z
x += 1
return list
iterator方法:
class fab(object):
'''
Iterator to produce Fibonacci
'''
def __init__(self,max):
self.max = max
self.x = 0
self.y = 0
self.z = 1
def __iter__(self):
return self
def __next__(self):
if self.x < self.max:
r = self.z
self.y,self.z = self.z,self.x + self.y + self.z
self.x += 1
return r
raise StopIteration('Done')
为了节省内存,和处于未知输出的考虑,使用迭代器来改善代码,但是迭代器什么都好,就是写起来不简洁。所以用 yield 来改写第三版。
Generator:
def fab(max):
x,y,z = 0,0,1
while x < max:
yield z
y,z = z,x+y+z
x += 1
for x in fab(8):
print(x)
三、装饰器
装饰器(Decorator)是python中最吸引人的特性,装饰器本质上还是一个函数,它可以让已有的函数不做任何改动的情况下增加功能。
非常适合有切面需求的场景,比如权限校验,日志记录和性能测试等等。比如你想要执行某个函数前记录日志或者记录时间来统计性能,又不想改动这个函数,就可以通过装饰器来实现。
1.定义:增强函数或类的功能的一个函数。
2.作用:增强函数的功能
# 伪代码
def decorator(func):
def wrapper(*args, **kwargs):
# 可以自定义传入的参数
# *args:将函数传入的参数存储在元组类型的变量args中
# **kwargs:将函数的参数和值存储在字典类型的变量kwargs中
print(func.__name__)
# 返回传入的方法名参数的调用
return func(*args, **kwargs)
# 返回内层函数的函数名
return wrapper
@decorator
def f():
pass
f()
3.装饰器可以传参,也可以不传参
# 装饰器
def decorator(func): # 嵌套函数
def wrap(*args, **kwargs):
print('Start!')
func(*args, **kwargs) # 类似于闭包,但是没有外部环境
print('End!')
return wrap
@decorator # 放在需要装饰的函数前
def f1(name):
print('My name is ' + name)
f1('hqh') # 调用方式不变
实际上是对装饰器的一个函数封装,并返回一个装饰器。可以把它看成一个带参数的闭包。
有了装饰器,我们就可以剥离出大量与函数功能本身无关的代码,增加了代码的重用性。
部分理论引用于:https://www.jianshu.com/p/efaa19594cf4